From 130b6eefd7b9e3eb39e8584ed82c3f929b120abc Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu <44787782+basusoumyadeep@users.noreply.github.com> Date: Sat, 25 Apr 2026 14:19:13 +0200 Subject: [PATCH 01/10] restructure code[skip ci] --- CMakeLists.txt | 5 + README.md | 112 +- ...CrossSpeciesComparisonGeneDetectPlugin.cpp | 17 +- src/SettingsAction.Analysis.inl | 2267 ++++++++ src/SettingsAction.Data.inl | 749 +++ src/SettingsAction.Serialization.inl | 115 + src/SettingsAction.Tree.inl | 287 + src/SettingsAction.Ui.inl | 1219 +++++ src/SettingsAction.cpp | 4599 +---------------- 9 files changed, 4823 insertions(+), 4547 deletions(-) create mode 100644 src/SettingsAction.Analysis.inl create mode 100644 src/SettingsAction.Data.inl create mode 100644 src/SettingsAction.Serialization.inl create mode 100644 src/SettingsAction.Tree.inl create mode 100644 src/SettingsAction.Ui.inl diff --git a/CMakeLists.txt b/CMakeLists.txt index d82e84c..c1959e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,11 @@ set(PLUGIN_SOURCES src/CrossSpeciesComparisonGeneDetectPlugin.cpp src/SettingsAction.h src/SettingsAction.cpp + src/SettingsAction.Ui.inl + src/SettingsAction.Analysis.inl + src/SettingsAction.Data.inl + src/SettingsAction.Tree.inl + src/SettingsAction.Serialization.inl src/CrossSpeciesComparisonGeneDetectPlugin.json ) set(LIBS diff --git a/README.md b/README.md index 2d0c025..b2c7302 100644 --- a/README.md +++ b/README.md @@ -1 +1,111 @@ -CrossSpeciesComparisonGeneDetectPlugin +# CrossSpeciesComparisonGeneDetectPlugin + +`CrossSpeciesComparisonGeneDetectPlugin` is a ManiVault view plugin for exploring cross-species gene expression differences from point-based single-cell style datasets. + +The plugin is designed for workflows where a user: + +- selects a subset of cells in an embedding or scatterplot, +- compares selected versus non-selected cells across species, +- ranks genes by differential expression, +- inspects per-species statistics in tables, +- and projects those statistics back onto phylogenetic tree and scatterplot views. + +## What The Plugin Does + +At a high level, the plugin combines five pieces of information: + +- a main expression dataset (`Points`), +- a low-dimensional embedding dataset (`Points`), +- a species assignment dataset (`Cluster`), +- one or more hierarchy / cell-type cluster datasets (`Cluster`), +- and a reference phylogenetic tree dataset (`CrossSpeciesComparisonTree`). + +From these inputs, it computes: + +- mean expression per gene and species, +- selected-versus-non-selected expression statistics, +- top-N ranked genes per species, +- species-level abundance summaries within selected hierarchy branches, +- table models for ranked genes and species summaries, +- optional scatterplot coloring datasets, +- and optional tree payloads for hierarchy-specific exploration. + +## Repository Layout + +- [CMakeLists.txt](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/CMakeLists.txt): CMake target definition and ManiVault/Qt integration. +- [src/CrossSpeciesComparisonGeneDetectPlugin.h](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/CrossSpeciesComparisonGeneDetectPlugin.h): plugin class declaration. +- [src/CrossSpeciesComparisonGeneDetectPlugin.cpp](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/CrossSpeciesComparisonGeneDetectPlugin.cpp): plugin wiring, view integration, table rendering, and scatterplot/tree coordination. +- [src/SettingsAction.h](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.h): main state container and action declarations. +- [src/SettingsAction.cpp](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.cpp): shared helpers and compilation unit for the split inline implementation. +- [src/SettingsAction.Ui.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Ui.inl): UI/action wiring. +- [src/SettingsAction.Analysis.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Analysis.inl): main analysis and ranking logic. +- [src/SettingsAction.Data.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Data.inl): dataset creation, model generation, and export helpers. +- [src/SettingsAction.Tree.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Tree.inl): tree-related logic. +- [src/SettingsAction.Serialization.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Serialization.inl): serialization support. + +## Build Requirements + +The project expects a ManiVault development environment with: + +- CMake 3.22 or newer, +- Qt 6 with `Widgets`, `WebEngineWidgets`, and `Concurrent`, +- ManiVault packages for `Core`, `PointData`, and `ClusterData`, +- the `CrossSpeciesComparisonTreeData` plugin/library available at build or install time. + +Relevant CMake variables: + +- `ManiVault_INSTALL_DIR`: ManiVault installation root. +- `MV_CSCTD_INSTALL_DIR`: installation root for `CrossSpeciesComparisonTreeData`. +- `CROSSSPECIESCOMPARISONTREEDATA_LINK_LIBRARY`: optional explicit override for the tree data plugin library. + +## Building + +Typical CMake flow: + +```powershell +cmake -S . -B build -DManiVault_INSTALL_DIR="C:\Path\To\ManiVault" +cmake --build build --config Release +``` + +On successful build, the plugin is installed into the ManiVault `Plugins` directory via the post-build install step defined in [CMakeLists.txt](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/CMakeLists.txt). + +## Runtime Data Expectations + +The plugin assumes: + +- the selected species and hierarchy cluster datasets are children of the main points dataset, +- the embedding dataset is also a child of the main points dataset, +- point dimension names correspond to gene identifiers, +- the reference tree contains the same species names as the species cluster dataset. + +If these relationships are missing or inconsistent, the plugin will refuse to compute and log diagnostic messages. + +## Performance Notes + +Recent optimization work focused on reducing full recomputation cost and UI overhead: + +- chunked matrix reads are used instead of fetching one gene at a time, +- selected-cell statistics are aggregated in-memory after a single chunked read over the selected points, +- repeated linear lookups in selection remapping and hierarchy membership checks were replaced with hashed or linear-time helpers, +- forced waits around t-SNE stop actions were removed, +- expensive UI auto-sizing and duplicate signal connections were reduced. + +Even with those changes, some operations can still be costly on very large datasets because the plugin may need to: + +- rebuild large table models, +- update multiple derived ManiVault datasets, +- refresh scatterplot color/selection state, +- and serialize many tree payloads for hierarchy exploration. + +## Maintenance Notes + +If you extend the analysis logic, prefer: + +- batched point reads over per-gene calls, +- explicit invalidation/caching boundaries, +- stable table widths over repeated `resizeColumnsToContents()` calls in hot paths, +- and avoiding repeated signal connections inside methods that run on every update. + +## License / Ownership + +This repository currently does not declare a license in the root. Add one if the project is intended for redistribution or external collaboration. diff --git a/src/CrossSpeciesComparisonGeneDetectPlugin.cpp b/src/CrossSpeciesComparisonGeneDetectPlugin.cpp index f2dc471..71f6ebc 100644 --- a/src/CrossSpeciesComparisonGeneDetectPlugin.cpp +++ b/src/CrossSpeciesComparisonGeneDetectPlugin.cpp @@ -1421,6 +1421,9 @@ void CrossSpeciesComparisonGeneDetectPlugin::modifyListData() proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); proxyModel->setFilterKeyColumn(0); _settingsAction.getGeneTableView()->setModel(proxyModel); + // This method runs on every recomputation, so we clear earlier hooks + // before wiring search and selection behavior again. + disconnect(_settingsAction.getSearchBox(), nullptr, this, nullptr); connect(_settingsAction.getSearchBox(), &CustomLineEdit::textboxSelectedForTyping, this, [this, proxyModel, model]() { makeAllRowsVisible(_settingsAction.getGeneTableView(),proxyModel); }); @@ -1458,13 +1461,17 @@ void CrossSpeciesComparisonGeneDetectPlugin::modifyListData() } // Sort and update the table view - model->sort(1, Qt::DescendingOrder); - _settingsAction.getGeneTableView()->resizeColumnsToContents(); + proxyModel->sort(1, Qt::DescendingOrder); + _settingsAction.getGeneTableView()->setColumnWidth(0, 85); + _settingsAction.getGeneTableView()->setColumnWidth(1, 80); + _settingsAction.getGeneTableView()->setColumnWidth(2, 220); + _settingsAction.getGeneTableView()->setColumnWidth(3, 420); _settingsAction.getGeneTableView()->update(); //disconnect(_settingsAction.getGeneTableView()->selectionModel(), &QItemSelectionModel::currentChanged, this, nullptr); + disconnect(_settingsAction.getGeneTableView()->selectionModel(), nullptr, this, nullptr); connect(_settingsAction.getGeneTableView()->selectionModel(), &QItemSelectionModel::currentChanged, [this](const QModelIndex& current, const QModelIndex& previous) { if (!current.isValid()) return; @@ -2088,8 +2095,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::selectedCellCountStatusBarAdd() _settingsAction.getSelectionDetailsTable()->hideColumn(2); } - // Resize columns - _settingsAction.getSelectionDetailsTable()->resizeColumnsToContents(); + // Fixed widths are cheaper than recomputing content widths on every refresh. _settingsAction.getSelectionDetailsTable()->setColumnWidth(1, 70); _settingsAction.getSelectionDetailsTable()->setColumnWidth(2, 90); _settingsAction.getSelectionDetailsTable()->setColumnWidth(3, 70); @@ -2545,7 +2551,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::selectedCellStatisticsStatusBarAdd( if (singleColumn) { _settingsAction.getSelectionDetailsTable()->hideColumn(4); } - _settingsAction.getSelectionDetailsTable()->resizeColumnsToContents(); + // Keep widths stable to avoid an extra full-table measurement pass. //_settingsAction.getSelectionDetailsTable()->setColumnWidth(0, 100); _settingsAction.getSelectionDetailsTable()->setColumnWidth(1, 85); _settingsAction.getSelectionDetailsTable()->setColumnWidth(2, 85); @@ -2562,6 +2568,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::selectedCellStatisticsStatusBarAdd( emit model->layoutChanged(); // Selection handling code + disconnect(_settingsAction.getSelectionDetailsTable()->selectionModel(), nullptr, this, nullptr); connect(_settingsAction.getSelectionDetailsTable()->selectionModel(), &QItemSelectionModel::selectionChanged, [this](const QItemSelection& selected, const QItemSelection& deselected) { static QModelIndex lastSelectedIndex; diff --git a/src/SettingsAction.Analysis.inl b/src/SettingsAction.Analysis.inl new file mode 100644 index 0000000..4eb2413 --- /dev/null +++ b/src/SettingsAction.Analysis.inl @@ -0,0 +1,2267 @@ +/* +void SettingsAction::triggerTrippleHierarchyFrequencyChange() +{ + if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) + { + return; + } + _clusterSpeciesFrequencyMap.clear(); + auto startTimer = std::chrono::high_resolution_clock::now(); + qDebug() << "computeFrequencyMapForHierarchyItemsChange for all 3 levels Start"; + + if (!_speciesNamesDataset.getCurrentDataset().isValid() || !_mainPointsDataset.getCurrentDataset().isValid() || !_topClusterNamesDataset.getCurrentDataset().isValid() || !_middleClusterNamesDataset.getCurrentDataset().isValid() || !_bottomClusterNamesDataset.getCurrentDataset().isValid()) { + qDebug() << "Datasets are not valid"; + return; + } + + auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); + auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); + auto numOfPoints = mainPointDatasetFull->getNumPoints(); + std::vector topClusterNames(numOfPoints, true); + std::vector middleClusterNames(numOfPoints, true); + std::vector bottomClusterNames(numOfPoints, true); + QStringList topInclusionList; + QStringList middleInclusionList; + QStringList bottomInclusionList; + auto topClusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); + auto middleClusterDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); + auto bottomClusterDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); + + auto processTopClusters = [&]() { + if (topClusterDataset.isValid()) + { + for (const auto& cluster : topClusterDataset->getClusters()) + { + if (!topInclusionList.contains(cluster.getName())) + { + for (const auto& index : cluster.getIndices()) + { + topClusterNames[index] = false; + } + } + } + } + }; + + auto processMiddleClusters = [&]() { + if (middleClusterDataset.isValid()) + { + for (const auto& cluster : middleClusterDataset->getClusters()) + { + if (!middleInclusionList.contains(cluster.getName())) + { + for (const auto& index : cluster.getIndices()) + { + middleClusterNames[index] = false; + } + } + } + } + }; + + auto processBottomClusters = [&]() { + if (bottomClusterDataset.isValid()) + { + for (const auto& cluster : bottomClusterDataset->getClusters()) + { + if (!bottomInclusionList.contains(cluster.getName())) + { + for (const auto& index : cluster.getIndices()) + { + bottomClusterNames[index] = false; + } + } + } + } + }; + + // Run the three tasks in parallel + QFuture topFuture = QtConcurrent::run(processTopClusters); + QFuture middleFuture = QtConcurrent::run(processMiddleClusters); + QFuture bottomFuture = QtConcurrent::run(processBottomClusters); + + // Wait for all tasks to complete + topFuture.waitForFinished(); + middleFuture.waitForFinished(); + bottomFuture.waitForFinished(); + + if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) + { + auto speciesclusters = speciesClusterDatasetFull->getClusters(); + for (const auto& species : speciesclusters) { + auto speciesIndices = species.getIndices(); + auto speciesName = species.getName(); + int topCount = std::count_if(speciesIndices.begin(), speciesIndices.end(), [&topClusterNames](int index) { + return topClusterNames[index]; + }); + int middleCount = std::count_if(speciesIndices.begin(), speciesIndices.end(), [&middleClusterNames](int index) { + return middleClusterNames[index]; + }); + int bottomCount = std::count_if(speciesIndices.begin(), speciesIndices.end(), [&bottomClusterNames](int index) { + return bottomClusterNames[index]; + }); + + _clusterSpeciesFrequencyMap[speciesName]["topCells"] = topCount; + _clusterSpeciesFrequencyMap[speciesName]["middleCells"] = middleCount; + _clusterSpeciesFrequencyMap[speciesName]["bottomCells"] = bottomCount; + } + } + + auto endTimer = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(endTimer - startTimer).count(); + qDebug() << "Time taken for computeFrequencyMapForHierarchyItemsChange for all 3 levels: " + QString::number(duration / 1000.0) + " s"; +} +*/ +void SettingsAction::updateButtonTriggered() +{ + if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) + { + qDebug() << "Map for hierarchy items change method stop for project load blocker is checked"; + return; + } + + try { + // _startComputationTriggerAction.setDisabled(true); + startCodeTimer("UpdateGeneFilteringTrigger"); + //startCodeTimer("Part1"); + + int groupIDDeletion = 10; + int groupID1 = 10 * 2; + int groupID2 = 10 * 3; + clearTemporaryDatasetHandles(); + removeDatasets(groupIDDeletion); + auto pointsDataset = _mainPointsDataset.getCurrentDataset(); + auto embeddingDataset = _embeddingDataset.getCurrentDataset(); + auto speciesDataset = _speciesNamesDataset.getCurrentDataset(); + auto clusterDataset = _bottomClusterNamesDataset.getCurrentDataset(); + auto referenceTreeDataset = _referenceTreeDataset.getCurrentDataset(); + _selectedSpeciesVals.setString(""); + _geneNamesConnection.setString(""); + bool isValid = false; + + QString referenceTreedatasetId = ""; + //stopCodeTimer("Part1"); + //startCodeTimer("Part2"); + if (!pointsDataset.isValid() || !embeddingDataset.isValid() || !speciesDataset.isValid() || !clusterDataset.isValid() || !referenceTreeDataset.isValid()) + { + qDebug() << "No datasets selected"; + //_startComputationTriggerAction.setDisabled(false); + return; + } + if (pointsDataset->getSelectionIndices().size() < 1) + { + qDebug() << "No points selected"; + //_startComputationTriggerAction.setDisabled(false); + return; + } + /*if (_selectedPointsTSNEDataset.isValid()) + { + _selectedPointsTSNEDataset->setSelectionIndices({}); + }*/ + //stopCodeTimer("Part2"); + //startCodeTimer("Part3"); + _clusterNameToGeneNameToExpressionValue.clear(); + referenceTreedatasetId = referenceTreeDataset->getId(); + isValid = speciesDataset->getParent() == pointsDataset && clusterDataset->getParent() == pointsDataset && embeddingDataset->getParent() == pointsDataset; + if (!isValid) + { + qDebug() << "Datasets are not valid"; + //_startComputationTriggerAction.setDisabled(false); + return; + } + _selectedIndicesFromStorage.clear(); + _selectedIndicesFromStorage = pointsDataset->getSelectionIndices(); + + auto embeddingDatasetRaw = mv::data().getDataset(embeddingDataset->getId()); + auto pointsDatasetRaw = mv::data().getDataset(pointsDataset->getId()); + auto pointsDatasetallColumnNameList = pointsDatasetRaw->getDimensionNames(); + auto embeddingDatasetallColumnNameList = embeddingDatasetRaw->getDimensionNames(); + //stopCodeTimer("Part3"); + //startCodeTimer("Part4"); + std::vector embeddingDatasetColumnIndices(embeddingDatasetallColumnNameList.size()); + std::iota(embeddingDatasetColumnIndices.begin(), embeddingDatasetColumnIndices.end(), 0); + + std::vector pointsDatasetallColumnIndices(pointsDatasetallColumnNameList.size()); + std::iota(pointsDatasetallColumnIndices.begin(), pointsDatasetallColumnIndices.end(), 0); + //stopCodeTimer("Part4"); + { + + if (_selectedIndicesFromStorage.size() > 0 && embeddingDatasetColumnIndices.size() > 0) + { + //startCodeTimer("Part5"); + auto speciesDatasetRaw = mv::data().getDataset(speciesDataset->getId()); + auto clusterDatasetRaw = mv::data().getDataset(clusterDataset->getId()); + auto clusterDatasetName = clusterDatasetRaw->getGuiName(); + auto clustersValuesAll = clusterDatasetRaw->getClusters(); + auto speciesValuesAll = speciesDatasetRaw->getClusters(); + + std::map>> selectedClustersMap; + std::map>> selectedSpeciesMap; + //stopCodeTimer("Part5"); + if (!speciesValuesAll.empty() && !clustersValuesAll.empty()) + { + + //if (_selectedPointsTSNEDataset.isValid()) + //{ + //auto datasetIDLowRem = _selectedPointsTSNEDataset.getDatasetId(); + //mv::events().notifyDatasetAboutToBeRemoved(_selectedPointsTSNEDataset); + //mv::data().removeDataset(_selectedPointsTSNEDataset); + //mv::events().notifyDatasetRemoved(datasetIDLowRem, PointType); + //} + + // _selectedPointsDataset = Dataset(); + //_selectedPointsEmbeddingDataset = Dataset(); + //startCodeTimer("Part6.1"); + /*if (!_selectedPointsDataset.isValid()) + { + _selectedPointsDataset = mv::data().createDataset("Points", "SelectedPointsDataset"); + _selectedPointsDataset->setGroupIndex(10); + mv::events().notifyDatasetAdded(_selectedPointsDataset); + + }*/ + + pointsDatasetRaw->setSelectionIndices(_selectedIndicesFromStorage); + _selectedPointsDataset = pointsDatasetRaw->createSubsetFromSelection("SelectedPointsDataset"); + _selectedPointsDataset->setGroupIndex(groupIDDeletion); + + if (!_tsneDatasetExpressionColors.isValid()) + { + _tsneDatasetExpressionColors = mv::data().createDataset("Points", "TSNEDatasetExpressionColors", _selectedPointsDataset); + _tsneDatasetExpressionColors->setGroupIndex(groupIDDeletion); + mv::events().notifyDatasetAdded(_tsneDatasetExpressionColors); + + } + + embeddingDatasetRaw->setSelectionIndices(_selectedIndicesFromStorage); + _selectedPointsEmbeddingDataset = embeddingDatasetRaw->createSubsetFromSelection("TSNEDataset", _selectedPointsDataset); + _selectedPointsEmbeddingDataset->setGroupIndex(groupIDDeletion); + + + if (!_tsneDatasetSpeciesColors.isValid()) + { + _tsneDatasetSpeciesColors = mv::data().createDataset("Cluster", "TSNEDatasetSpeciesColors", _selectedPointsDataset); + _tsneDatasetSpeciesColors->setGroupIndex(groupIDDeletion); + mv::events().notifyDatasetAdded(_tsneDatasetSpeciesColors); + } + + if (!_tsneDatasetClusterColors.isValid()) + { + _tsneDatasetClusterColors = mv::data().createDataset("Cluster", "TSNEDatasetClusterColors", _selectedPointsDataset); + _tsneDatasetClusterColors->setGroupIndex(groupIDDeletion); + mv::events().notifyDatasetAdded(_tsneDatasetClusterColors); + } + + + if (!_filteredUMAPDatasetPoints.isValid()) + { + _filteredUMAPDatasetPoints = mv::data().createDataset("Points", "Filtered UMAP Dataset Points"); + _filteredUMAPDatasetPoints->setGroupIndex(groupID1); + mv::events().notifyDatasetAdded(_filteredUMAPDatasetPoints); + if (!_filteredUMAPDatasetColors.isValid()) + { + //need to delete + + } + if (!_filteredUMAPDatasetClusters.isValid()) + { + //need to delete + + } + _filteredUMAPDatasetColors = mv::data().createDataset("Points", "Filtered UMAP Dataset Colors", _filteredUMAPDatasetPoints); + _filteredUMAPDatasetColors->setGroupIndex(groupID1); + mv::events().notifyDatasetAdded(_filteredUMAPDatasetColors); + + _filteredUMAPDatasetClusters = mv::data().createDataset("Cluster", "Filtered UMAP Dataset Clusters", _filteredUMAPDatasetPoints); + _filteredUMAPDatasetClusters->setGroupIndex(groupID1); + mv::events().notifyDatasetAdded(_filteredUMAPDatasetClusters); + + } + + + + /*if (!_selectedPointsEmbeddingDataset.isValid()) + { + _selectedPointsEmbeddingDataset = mv::data().createDataset("Points", "TSNEDataset", _selectedPointsDataset); + _selectedPointsEmbeddingDataset->setGroupIndex(10); + mv::events().notifyDatasetAdded(_selectedPointsEmbeddingDataset); + + }*/ + + + if (!_geneSimilarityPoints.isValid()) + { + _geneSimilarityPoints = mv::data().createDataset("Points", "GeneSimilarityPoints"); + _geneSimilarityPoints->setGroupIndex(groupID2); + mv::events().notifyDatasetAdded(_geneSimilarityPoints); + } + if (!_geneSimilarityClusterColoring.isValid()) + { + _geneSimilarityClusterColoring = mv::data().createDataset("Cluster", "GeneSimilarityClusterColoring", _geneSimilarityPoints); + _geneSimilarityClusterColoring->setGroupIndex(groupID2); + mv::events().notifyDatasetAdded(_geneSimilarityClusterColoring); + + } + //_geneSimilarityClusters.clear(); + //stopCodeTimer("Part6.1"); + if (_selectedPointsDataset.isValid() && _selectedPointsEmbeddingDataset.isValid() && _tsneDatasetSpeciesColors.isValid() && _tsneDatasetClusterColors.isValid() && _geneSimilarityPoints.isValid() && _geneSimilarityClusterColoring.isValid()) + { + //startCodeTimer("Part6.2"); + //_tsneDatasetSpeciesColors->getClusters() = QVector(); + //events().notifyDatasetDataChanged(_tsneDatasetSpeciesColors); + //_tsneDatasetClusterColors->getClusters() = QVector(); + //events().notifyDatasetDataChanged(_tsneDatasetClusterColors); + _geneSimilarityClusterColoring->getClusters() = QVector(); + events().notifyDatasetDataChanged(_geneSimilarityClusterColoring); + //stopCodeTimer("Part6.2"); + //startCodeTimer("Part7"); + //startCodeTimer("Part7.1"); + int selectedIndicesFromStorageSize = static_cast(_selectedIndicesFromStorage.size()); + int pointsDatasetColumnsSize = static_cast(pointsDatasetallColumnIndices.size()); + int embeddingDatasetColumnsSize = static_cast(embeddingDatasetColumnIndices.size()); + //QString datasetIdEmb = _selectedPointsDataset->getId(); + //QString datasetId = _selectedPointsEmbeddingDataset->getId(); + int dimofDatasetExp = 1; + std::vector dimensionNamesExp = { "Expression" }; + QString datasetIdExp = _tsneDatasetExpressionColors->getId(); + //stopCodeTimer("Part7.1"); + //startCodeTimer("Part7.2"); + + // Define result containers outside the lambda functions to ensure they are accessible later + //std::vector resultContainerForSelectedPoints(selectedIndicesFromStorageSize * pointsDatasetColumnsSize); + //std::vector resultContainerForSelectedEmbeddingPoints(selectedIndicesFromStorageSize * embeddingDatasetColumnsSize); + std::vector resultContainerColorPoints(selectedIndicesFromStorageSize, -1.0f); + + //first thread start + //auto future1 = std::async(std::launch::async, [&]() { + //pointsDatasetRaw->populateDataForDimensions(resultContainerForSelectedPoints, pointsDatasetallColumnIndices, _selectedIndicesFromStorage); + // }); + + //second thread start + // auto future2 = std::async(std::launch::async, [&]() { + //embeddingDatasetRaw->populateDataForDimensions(resultContainerForSelectedEmbeddingPoints, embeddingDatasetColumnIndices, _selectedIndicesFromStorage); + // }); + + + // Wait for all futures to complete before proceeding + //future1.wait(); + //future2.wait(); + + + //startCodeTimer("Part7.2.1"); + //needs to wait for future1 finish only + //populatePointData(datasetIdEmb, resultContainerForSelectedPoints, selectedIndicesFromStorageSize, pointsDatasetColumnsSize, pointsDatasetallColumnNameList); + //stopCodeTimer("Part7.2.1"); + + //startCodeTimer("Part7.2.2"); + //needs to wait for future2 finish only + //populatePointData(datasetId, resultContainerForSelectedEmbeddingPoints, selectedIndicesFromStorageSize, embeddingDatasetColumnsSize, embeddingDatasetallColumnNameList); + //stopCodeTimer("Part7.2.2"); + + //startCodeTimer("Part7.2.3"); + //needs to wait for future3 finish only + populatePointData(datasetIdExp, resultContainerColorPoints, selectedIndicesFromStorageSize, dimofDatasetExp, dimensionNamesExp); + //stopCodeTimer("Part7.2.3"); + + //stopCodeTimer("Part7.2"); + + + //stopCodeTimer("Part7"); + //startCodeTimer("Part8"); + if (_selectedPointsTSNEDataset.isValid()) + { + auto runningAction = dynamic_cast(_selectedPointsTSNEDataset->findChildByPath("TSNE/TsneComputationAction/Running")); + + if (runningAction) + { + + if (runningAction->isChecked()) + { + auto stopAction = dynamic_cast(_selectedPointsTSNEDataset->findChildByPath("TSNE/TsneComputationAction/Stop")); + if (stopAction) + { + stopAction->trigger(); + QApplication::processEvents(); + } + } + + } + } + //stopCodeTimer("Part8"); + //startCodeTimer("Part9"); + if (!_performGeneTableTsneAction.isChecked()) + { + + + mv::plugin::AnalysisPlugin* analysisPlugin; + bool usePreTSNE = _usePreComputedTSNE.isChecked(); + + auto scatterplotModificationsLowDimUMAP = [this]() { + if (_selectedPointsTSNEDataset.isValid()) { + auto scatterplotViewFactory = mv::plugins().getPluginFactory("Scatterplot View"); + mv::gui::DatasetPickerAction* colorDatasetPickerAction; + mv::gui::DatasetPickerAction* pointDatasetPickerAction; + mv::gui::ViewPluginSamplerAction* samplerActionAction; + if (scatterplotViewFactory) { + for (auto plugin : mv::plugins().getPluginsByFactory(scatterplotViewFactory)) { + if (plugin->getGuiName() == "Scatterplot Cell Selection Overview") { + pointDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Position")); + if (pointDatasetPickerAction) { + pointDatasetPickerAction->setCurrentText(""); + + pointDatasetPickerAction->setCurrentDataset(_selectedPointsTSNEDataset); + + colorDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Color")); + if (colorDatasetPickerAction) + { + colorDatasetPickerAction->setCurrentText(""); + + + + auto selectedColorType = _scatterplotReembedColorOption.getCurrentText(); + if (selectedColorType != "") + { + if (selectedColorType == "Cluster") + { + if (_bottomClusterNamesDataset.getCurrentDataset().isValid()) + { + colorDatasetPickerAction->setCurrentDataset(_bottomClusterNamesDataset.getCurrentDataset()); + + auto legendViewFactory = mv::plugins().getPluginFactory("ChartLegend View"); + if (legendViewFactory) + { + for (auto legendPlugin : mv::plugins().getPluginsByFactory(legendViewFactory)) + { + if (legendPlugin->getGuiName() == "Legend View") + { + //legendPlugin->printChildren(); + auto legendDatasetPickerAction = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster dataset")); + if (legendDatasetPickerAction) + { + legendDatasetPickerAction->setCurrentDataset(_bottomClusterNamesDataset.getCurrentDataset()); + } + auto chartTitle = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Chart Title")); + if (chartTitle) + { + chartTitle->setString("Cell types"); + } + /* + auto selectionColor = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Selection color")); + if (selectionColor) + { + selectionColor->setColor(QColor(53, 126, 199)); + } + auto selectionStringDelimiter = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Delimiter")); + if (selectionStringDelimiter) + { + selectionStringDelimiter->setString(","); + } + auto selectionClustersString = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster Selection string")); + if (selectionClustersString) + { + selectionClustersString->setString(""); //TODO + } + */ + } + } + } + + + } + } + else if (selectedColorType == "Species") + { + if (_speciesNamesDataset.getCurrentDataset().isValid()) + { + colorDatasetPickerAction->setCurrentDataset(_speciesNamesDataset.getCurrentDataset()); + } + } + else if (selectedColorType == "Expression") + { + if (_tsneDatasetExpressionColors.isValid()) + { + colorDatasetPickerAction->setCurrentDataset(_tsneDatasetExpressionColors); + } + } + + + + } + } + + samplerActionAction = plugin->findChildByPath("Sampler"); + + if (samplerActionAction) + { + samplerActionAction->setHtmlViewGeneratorFunction([this](const ViewPluginSamplerAction::SampleContext& toolTipContext) -> QString { + QString clusterDatasetId = _speciesNamesDataset.getCurrentDataset().getDatasetId(); + return generateTooltip(toolTipContext, clusterDatasetId, true, "GlobalPointIndices"); + }); + } + } + } + } + } + } + + }; + //stopCodeTimer("Part9"); + if (!usePreTSNE) + { + //startCodeTimer("Part10"); + analysisPlugin = mv::plugins().requestPlugin("tSNE Analysis", { _selectedPointsEmbeddingDataset }); + if (!analysisPlugin) { + qDebug() << "Could not find create TSNE Analysis"; + return; + } + _selectedPointsTSNEDataset = analysisPlugin->getOutputDataset(); + _selectedPointsTSNEDataset->setGroupIndex(groupIDDeletion); + if (_selectedPointsTSNEDataset.isValid()) + { + + int perplexity = std::min(static_cast(_selectedIndicesFromStorage.size()), _tsnePerplexity.getValue()); + if (perplexity < 5) + { + qDebug() << "Perplexity is less than 5"; + //_startComputationTriggerAction.setDisabled(false); + return; + } + if (perplexity != _tsnePerplexity.getValue()) + { + _tsnePerplexity.setValue(perplexity); + } + + auto perplexityAction = dynamic_cast(_selectedPointsTSNEDataset->findChildByPath("TSNE/Perplexity")); + if (perplexityAction) + { + qDebug() << "Perplexity: Found"; + perplexityAction->setValue(perplexity); + } + else + { + qDebug() << "Perplexity: Not Found"; + } + + scatterplotModificationsLowDimUMAP(); + + auto startAction = dynamic_cast(_selectedPointsTSNEDataset->findChildByPath("TSNE/TsneComputationAction/Start")); + if (startAction) { + + startAction->trigger(); + + analysisPlugin->getOutputDataset()->setSelectionIndices({}); + } + + } + //stopCodeTimer("Part10"); + } + else + { + //startCodeTimer("Part11"); + auto umapDataset = _scatterplotEmbeddingPointsUMAPOption.getCurrentDataset(); + + if (umapDataset.isValid()) + { + + + _selectedPointsTSNEDataset = mv::data().createDerivedDataset("SelectedPointsTSNEDataset", _selectedPointsEmbeddingDataset, _selectedPointsEmbeddingDataset); + _selectedPointsTSNEDataset->setGroupIndex(groupIDDeletion); + mv::events().notifyDatasetAdded(_selectedPointsTSNEDataset); + + auto umapDatasetRaw = mv::data().getDataset(umapDataset->getId()); + auto dimNames = umapDatasetRaw->getDimensionNames(); + int preComputedEmbeddingColumnsSize = umapDatasetRaw->getNumDimensions(); + std::vector resultContainerPreComputedUMAP(selectedIndicesFromStorageSize * preComputedEmbeddingColumnsSize); + std::vector preComputedEmbeddingColumnIndices(preComputedEmbeddingColumnsSize); + + std::iota(preComputedEmbeddingColumnIndices.begin(), preComputedEmbeddingColumnIndices.end(), 0); + + umapDatasetRaw->populateDataForDimensions(resultContainerPreComputedUMAP, preComputedEmbeddingColumnIndices, _selectedIndicesFromStorage); + + QString datasetId = _selectedPointsTSNEDataset->getId(); + populatePointData(datasetId, resultContainerPreComputedUMAP, selectedIndicesFromStorageSize, preComputedEmbeddingColumnsSize, dimNames); + + if (_selectedPointsTSNEDataset.isValid()) + { + scatterplotModificationsLowDimUMAP(); + } + } + else + { + qDebug() << "UMAP Dataset not valid"; + } + + + //stopCodeTimer("Part11"); + + + + } + } + + + } + else + { + qDebug() << "Datasets are not valid"; + } + std::sort(_selectedIndicesFromStorage.begin(), _selectedIndicesFromStorage.end()); + + std::unordered_map selectedIndexLookup; + selectedIndexLookup.reserve(_selectedIndicesFromStorage.size()); + for (int selectedPosition = 0; selectedPosition < static_cast(_selectedIndicesFromStorage.size()); ++selectedPosition) { + selectedIndexLookup.emplace(static_cast(_selectedIndicesFromStorage[selectedPosition]), selectedPosition); + } + + //startCodeTimer("Part12"); + //startCodeTimer("Part12.1"); + QFuture futureClusterCVals = QtConcurrent::run([&]() { + for (auto& clusters : clustersValuesAll) { + const auto clusterIndices = clusters.getIndices(); + auto clusterName = clusters.getName(); + auto clusterColor = clusters.getColor(); + std::vector filteredIndices; + filteredIndices.reserve(clusterIndices.size()); + + for (const auto index : clusterIndices) { + auto selectedIndexIt = selectedIndexLookup.find(static_cast(index)); + if (selectedIndexIt != selectedIndexLookup.end()) { + filteredIndices.push_back(selectedIndexIt->second); + } + } + + selectedClustersMap[clusterName] = { clusterColor, std::move(filteredIndices) }; + } + }); + QFuture futureSpeciesCVals = QtConcurrent::run([&]() { + for (auto& clusters : speciesValuesAll) { + const auto clusterIndices = clusters.getIndices(); + auto clusterName = clusters.getName(); + auto clusterColor = clusters.getColor(); + std::vector filteredIndices; + filteredIndices.reserve(clusterIndices.size()); + + for (const auto index : clusterIndices) { + auto selectedIndexIt = selectedIndexLookup.find(static_cast(index)); + if (selectedIndexIt != selectedIndexLookup.end()) { + filteredIndices.push_back(selectedIndexIt->second); + } + } + + selectedSpeciesMap[clusterName] = { clusterColor, std::move(filteredIndices) }; + } + }); + futureClusterCVals.waitForFinished(); // Wait for the concurrent task to complete + futureSpeciesCVals.waitForFinished(); + //stopCodeTimer("Part12.1"); + + + + //startCodeTimer("Part12.2"); + //_currentHierarchyItemsTopForTable.clear(); + //_currentHierarchyItemsMiddleForTable.clear(); + QStringList inclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + const auto inclusionSet = toStringSet(inclusionList); + _currentHierarchyItemsMiddleForTable = QStringList{}; + if (_topSelectedHierarchyStatus.getString() != "") + { + + QStringList list = _topSelectedHierarchyStatus.getString().split(" @%$,$%@ "); + for (const auto& item : list) { + + if (inclusionSet.find(item) != inclusionSet.end()) + { + _currentHierarchyItemsMiddleForTable.push_back(item); + } + + } + } + else + { + _currentHierarchyItemsMiddleForTable = QStringList{}; + } + + + //_currentHierarchyItemsMiddleForTable = QSet{}; + // + /*for (const auto& [key, clusterIndicesMap] : _topHierarchyClusterMap) + { + + for (const auto& index : _selectedIndicesFromStorage) + { + if (clusterIndicesMap.at(index)) + { + + if (inclusionList.contains(key)) + { + _currentHierarchyItemsMiddleForTable.insert(key); + } + break; + } + } + }*/ + //qDebug() << "Middle Hierarchy Items: " << _currentHierarchyItemsMiddleForTable; + + // + + + // The expensive part of the update path is computing + // selected means for every gene across all affected species. + // We therefore separate cheap per-species metadata from the + // chunked matrix scan so the selected subset is read only once. + struct SpeciesComputationMeta { + QString name; + QColor color; + int allCellCount = 0; + int selectedCellCount = 0; + int nonSelectedCellsCount = 0; + int abundanceTop = 0; + int abundanceMiddle = 0; + int countAbundanceNumerator = 0; + }; + + std::vector speciesComputationMeta; + speciesComputationMeta.reserve(speciesValuesAll.size()); + + const int selectedIndicesFromStorageSize = static_cast(_selectedIndicesFromStorage.size()); + std::vector selectedPointSpeciesIndex(selectedIndicesFromStorageSize, -1); + std::vector> selectedGlobalIndicesPerSpecies; + selectedGlobalIndicesPerSpecies.resize(speciesValuesAll.size()); + + for (int speciesIndex = 0; speciesIndex < static_cast(speciesValuesAll.size()); ++speciesIndex) { + const auto& species = speciesValuesAll[speciesIndex]; + SpeciesComputationMeta meta; + meta.name = species.getName(); + meta.color = species.getColor(); + meta.allCellCount = static_cast(species.getIndices().size()); + speciesComputationMeta.push_back(meta); + } + + for (int speciesIndex = 0; speciesIndex < static_cast(speciesValuesAll.size()); ++speciesIndex) { + const auto& species = speciesValuesAll[speciesIndex]; + for (const auto globalIndex : species.getIndices()) { + auto selectedIndexIt = selectedIndexLookup.find(static_cast(globalIndex)); + if (selectedIndexIt != selectedIndexLookup.end()) { + const int selectedPosition = selectedIndexIt->second; + selectedPointSpeciesIndex[selectedPosition] = speciesIndex; + selectedGlobalIndicesPerSpecies[speciesIndex].push_back(static_cast(globalIndex)); + } + } + } + + for (int speciesIndex = 0; speciesIndex < static_cast(speciesComputationMeta.size()); ++speciesIndex) { + auto& meta = speciesComputationMeta[speciesIndex]; + meta.selectedCellCount = static_cast(selectedGlobalIndicesPerSpecies[speciesIndex].size()); + meta.nonSelectedCellsCount = meta.allCellCount - meta.selectedCellCount; + } + + for (int speciesIndex = 0; speciesIndex < static_cast(speciesValuesAll.size()); ++speciesIndex) { + const auto speciesIndices = speciesValuesAll[speciesIndex].getIndices(); + auto& meta = speciesComputationMeta[speciesIndex]; + + for (const auto& cluster : _topHierarchyClusterMap) { + if (inclusionSet.find(cluster.first) == inclusionSet.end()) { + continue; + } + + bool clusterPresent = false; + const auto& currentInclusionClusterMap = cluster.second; + int clusterSize = 0; + + for (const auto speciesPointIndex : speciesIndices) { + if (currentInclusionClusterMap[speciesPointIndex]) { + clusterSize++; + } + } + + for (const auto selectedGlobalIndex : selectedGlobalIndicesPerSpecies[speciesIndex]) { + if (currentInclusionClusterMap[selectedGlobalIndex]) { + meta.countAbundanceNumerator++; + clusterPresent = true; + } + } + + meta.abundanceTop += clusterSize; + if (clusterPresent) { + meta.abundanceMiddle += clusterSize; + } + } + } + + _clusterNameToGeneNameToExpressionValue.clear(); + _selectedSpeciesCellCountMap.clear(); + + for (const auto& meta : speciesComputationMeta) { + _clusterNameToGeneNameToExpressionValue[meta.name] = {}; + auto& countInfo = _selectedSpeciesCellCountMap[meta.name]; + countInfo.color = meta.color; + countInfo.selectedCellsCount = meta.selectedCellCount; + countInfo.nonSelectedCellsCount = meta.nonSelectedCellsCount; + countInfo.abundanceMiddle = meta.abundanceMiddle; + countInfo.abundanceTop = meta.abundanceTop; + countInfo.countAbundanceNumerator = meta.countAbundanceNumerator; + } + + const int speciesCount = static_cast(speciesComputationMeta.size()); + + if (_selectedIndicesFromStorage.empty()) { + // Preserve the legacy behavior for an empty selection: + // selected mean becomes 0 and the non-selected mean falls + // back to the species-wide baseline. + for (int speciesIndex = 0; speciesIndex < speciesCount; ++speciesIndex) { + const auto& meta = speciesComputationMeta[speciesIndex]; + const auto speciesMeanMapIt = _clusterGeneMeanExpressionMap.find(meta.name); + if (speciesMeanMapIt == _clusterGeneMeanExpressionMap.end()) { + continue; + } + + auto& speciesExpressionMap = _clusterNameToGeneNameToExpressionValue[meta.name]; + for (int geneIndex = 0; geneIndex < static_cast(pointsDatasetallColumnNameList.size()); ++geneIndex) { + const auto& geneName = pointsDatasetallColumnNameList[geneIndex]; + float allCellMean = 0.0f; + if (const auto geneMeanIt = speciesMeanMapIt->second.find(geneName); geneMeanIt != speciesMeanMapIt->second.end()) { + allCellMean = geneMeanIt->second.second; + } + + Stats valueStats; + valueStats.abundanceMiddle = meta.abundanceMiddle; + valueStats.abundanceTop = meta.abundanceTop; + valueStats.countSelected = 0; + valueStats.countNonSelected = meta.allCellCount; + valueStats.meanSelected = 0.0f; + valueStats.meanNonSelected = allCellMean; + valueStats.color = meta.color; + valueStats.countAbundanceNumerator = meta.countAbundanceNumerator; + speciesExpressionMap[geneName] = valueStats; + } + } + } + else { + for (int startGeneIndex = 0; startGeneIndex < static_cast(pointsDatasetallColumnIndices.size()); startGeneIndex += kGeneChunkSize) { + // Read all selected points for a chunk of genes once, + // then fan the rows back out to species-specific sums. + const int chunkSize = std::min(kGeneChunkSize, static_cast(pointsDatasetallColumnIndices.size()) - startGeneIndex); + std::vector geneChunkIndices(pointsDatasetallColumnIndices.begin() + startGeneIndex, pointsDatasetallColumnIndices.begin() + startGeneIndex + chunkSize); + std::vector selectedChunkData(static_cast(selectedIndicesFromStorageSize) * static_cast(chunkSize), 0.0f); + pointsDatasetRaw->populateDataForDimensions(selectedChunkData, geneChunkIndices, _selectedIndicesFromStorage); + + std::vector> speciesChunkSums(speciesCount, std::vector(chunkSize, 0.0)); + for (int selectedRowIndex = 0; selectedRowIndex < selectedIndicesFromStorageSize; ++selectedRowIndex) { + const int speciesIndex = selectedPointSpeciesIndex[selectedRowIndex]; + if (speciesIndex < 0) { + continue; + } + + const int rowOffset = selectedRowIndex * chunkSize; + auto& currentSpeciesChunkSums = speciesChunkSums[speciesIndex]; + for (int geneOffset = 0; geneOffset < chunkSize; ++geneOffset) { + currentSpeciesChunkSums[geneOffset] += selectedChunkData[rowOffset + geneOffset]; + } + } + + for (int speciesIndex = 0; speciesIndex < speciesCount; ++speciesIndex) { + const auto& meta = speciesComputationMeta[speciesIndex]; + const auto speciesMeanMapIt = _clusterGeneMeanExpressionMap.find(meta.name); + if (speciesMeanMapIt == _clusterGeneMeanExpressionMap.end()) { + continue; + } + + auto& speciesExpressionMap = _clusterNameToGeneNameToExpressionValue[meta.name]; + const auto& currentSpeciesChunkSums = speciesChunkSums[speciesIndex]; + for (int geneOffset = 0; geneOffset < chunkSize; ++geneOffset) { + const int geneIndex = geneChunkIndices[geneOffset]; + const auto& geneName = pointsDatasetallColumnNameList[geneIndex]; + + float allCellMean = 0.0f; + if (const auto geneMeanIt = speciesMeanMapIt->second.find(geneName); geneMeanIt != speciesMeanMapIt->second.end()) { + allCellMean = geneMeanIt->second.second; + } + + const float selectedCellsMean = (meta.selectedCellCount > 0) + ? static_cast(currentSpeciesChunkSums[geneOffset] / static_cast(meta.selectedCellCount)) + : 0.0f; + + float nonSelectedMean = allCellMean; + if (meta.selectedCellCount > 0 && meta.nonSelectedCellsCount > 0) { + const float allCellTotal = allCellMean * meta.allCellCount; + nonSelectedMean = (allCellTotal - (selectedCellsMean * meta.selectedCellCount)) / meta.nonSelectedCellsCount; + } + + Stats valueStats; + valueStats.abundanceMiddle = meta.abundanceMiddle; + valueStats.abundanceTop = meta.abundanceTop; + valueStats.countSelected = meta.selectedCellCount; + valueStats.countNonSelected = meta.nonSelectedCellsCount; + valueStats.meanSelected = selectedCellsMean; + valueStats.meanNonSelected = nonSelectedMean; + valueStats.color = meta.color; + valueStats.countAbundanceNumerator = meta.countAbundanceNumerator; + speciesExpressionMap[geneName] = valueStats; + } + } + } + } + + + + + //stopCodeTimer("Part12.2"); + + auto clusterColorDatasetId = _tsneDatasetClusterColors->getId(); + auto speciesColorDatasetId = _tsneDatasetSpeciesColors->getId(); + //startCodeTimer("Part12.3"); + populateClusterData(speciesColorDatasetId, selectedSpeciesMap); + //stopCodeTimer("Part12.3"); + //startCodeTimer("Part12.4"); + populateClusterData(clusterColorDatasetId, selectedClustersMap); + //stopCodeTimer("Part12.4"); + //stopCodeTimer("Part12"); + updateClusterInfoStatusBar(); + /* + QLayoutItem* layoutItem; + while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { + delete layoutItem->widget(); + delete layoutItem; + } + if (_tsneDatasetClusterColors.isValid()) + { + + auto clusterValues = _tsneDatasetClusterColors->getClusters(); + if (!clusterValues.empty()) + { + //startCodeTimer("Part13"); + + //QLayoutItem* layoutItem; + //while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { + // delete layoutItem->widget(); + // delete layoutItem; + //} + + // Create a description label + auto descriptionLabel = new QLabel("Selected Cell Counts per " + clusterDatasetName + " :"); + // Optionally, set a stylesheet for the description label for styling + descriptionLabel->setStyleSheet("QLabel { font-weight: bold; padding: 2px; }"); + // Add the description label to the layout + _selectedCellClusterInfoStatusBar->addWidget(descriptionLabel); + + + std::vector orderedClustersSet; + + for (const auto& cluster : clusterValues) { + ClusterOrderContainer temp{ + cluster.getIndices().size(), + cluster.getColor(), + cluster.getName() + }; + orderedClustersSet.push_back(std::move(temp)); + } + + const auto& currentText = _clusterCountSortingType.getCurrentText(); + if (currentText == "Name") { + std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), sortByName); + } + else if (currentText == "Hierarchy View" && !_customOrderClustersFromHierarchy.empty()) { + if (_customOrderClustersFromHierarchyMap.empty()) { + _customOrderClustersFromHierarchyMap = prepareCustomSortMap(_customOrderClustersFromHierarchy); + } + std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), [&](const ClusterOrderContainer& a, const ClusterOrderContainer& b) { + return sortByCustomList(a, b, _customOrderClustersFromHierarchyMap); + }); + } + else { + std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), sortByCount); + if (currentText != "Count") { + _clusterCountSortingType.setCurrentText("Count"); + } + } + + for (const auto& clustersFromSet : orderedClustersSet) + { + auto clusterLabel = new QLabel(QString("%1: %2").arg(clustersFromSet.name).arg(clustersFromSet.count)); + QColor textColor = clustersFromSet.color.lightness() > 127 ? Qt::black : Qt::white; + clusterLabel->setStyleSheet(QString("QLabel { color: %1; background-color: %2; padding: 2px; border: 0.5px solid %3; }") + .arg(textColor.name()).arg(clustersFromSet.color.name(QColor::HexArgb)).arg(textColor.name())); + _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); + } + + + + + + //for (auto cluster : clusterValues) { + // auto clusterName = cluster.getName(); + // auto clusterIndicesSize = cluster.getIndices().size(); + // auto clusterColor = cluster.getColor(); // Assuming getColor() returns a QColor + + // // Calculate luminance + // qreal luminance = 0.299 * clusterColor.redF() + 0.587 * clusterColor.greenF() + 0.114 * clusterColor.blueF(); + + // // Choose text color based on luminance + // QString textColor = (luminance > 0.5) ? "black" : "white"; + + // // Convert QColor to hex string for stylesheet + // QString backgroundColor = clusterColor.name(QColor::HexArgb); + + // auto clusterLabel = new QLabel(QString("%1: %2").arg(clusterName).arg(clusterIndicesSize)); + // // Add text color and background color to clusterLabel with padding and border for better styling + // clusterLabel->setStyleSheet(QString("QLabel { color: %1; background-color: %2; padding: 2px; border: 0.5px solid %3; }") + // .arg(textColor).arg(backgroundColor).arg(textColor)); + // _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); + //} + + + } + + } + */ + //the next line should only execute if all above are finished + + + //startCodeTimer("Part14"); + findTopNGenesPerCluster(); + //stopCodeTimer("Part14"); + + + + + } + + else + { + qDebug() << "Species or Clusters are empty"; + } + + + } + + else + { + qDebug() << "No points selected or no dimensions present"; + } + + _removeRowSelection.trigger(); + _removeRowSelection.setEnabled(false); + //enableDisableButtonsAutomatically(); + + } + stopCodeTimer("UpdateGeneFilteringTrigger"); + //_startComputationTriggerAction.setDisabled(false); + } + catch (const std::exception& e) { + qDebug() << "An exception occurred in coputation: " << e.what(); + _statusColorAction.setString("E"); + } + catch (...) { + qDebug() << "An unknown exception occurred in coputation"; + _statusColorAction.setString("E"); + } +} + +void SettingsAction::updateClusterInfoStatusBar() +{ + QLayoutItem* layoutItem; + while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { + delete layoutItem->widget(); + delete layoutItem; + } + if (_tsneDatasetClusterColors.isValid() && _bottomClusterNamesDataset.getCurrentDataset().isValid()) + { + auto clusterDatasetName = _bottomClusterNamesDataset.getCurrentDataset()->getGuiName(); + auto clusterValues = _tsneDatasetClusterColors->getClusters(); + if (!clusterValues.empty()) + { + //startCodeTimer("Part13"); + + /*QLayoutItem* layoutItem; + while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { + delete layoutItem->widget(); + delete layoutItem; + }*/ + + // Create a description label + auto descriptionLabel = new QLabel("Cell counts per " + clusterDatasetName + ", sorted by " + _clusterCountSortingType.getCurrentText() + ":"); + + // Optionally, set a stylesheet for the description label for styling + descriptionLabel->setStyleSheet("QLabel { font-weight: bold; padding: 2px; }"); + // Add the description label to the layout + _selectedCellClusterInfoStatusBar->addWidget(descriptionLabel); + + + std::vector orderedClustersSet; + + for (const auto& cluster : clusterValues) { + ClusterOrderContainer temp{ + static_cast(cluster.getIndices().size()), + cluster.getColor(), + cluster.getName() + }; + orderedClustersSet.push_back(std::move(temp)); + } + + const auto& currentText = _clusterCountSortingType.getCurrentText(); + if (currentText == "Name") { + std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), sortByName); + } + else if (currentText == "Hierarchy View" && !_customOrderClustersFromHierarchy.empty()) { + if (_customOrderClustersFromHierarchyMap.empty()) { + _customOrderClustersFromHierarchyMap = prepareCustomSortMap(_customOrderClustersFromHierarchy); + } + std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), [&](const ClusterOrderContainer& a, const ClusterOrderContainer& b) { + return sortByCustomList(a, b, _customOrderClustersFromHierarchyMap); + }); + } + else { + std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), sortByCount); + if (currentText != "Count") { + _clusterCountSortingType.setCurrentText("Count"); + } + } + QString selectedClustersString = ""; + for (const auto& clustersFromSet : orderedClustersSet) + { + auto clusterLabel = new ClickableLabel(); // Create the label without text + QString labelText = QString("%1: %2").arg(clustersFromSet.name).arg(clustersFromSet.count); + clusterLabel->setText(labelText); // Set the text on the label + selectedClustersString = selectedClustersString + clustersFromSet.name + ","; + QColor textColor = clustersFromSet.color.lightness() > 127 ? Qt::black : Qt::white; + clusterLabel->setStyleSheet(QString("ClickableLabel { color: %1; background-color: %2; padding: 2px; border: 0.5px solid %3; }") + .arg(textColor.name()).arg(clustersFromSet.color.name(QColor::HexArgb)).arg(textColor.name())); + connect(clusterLabel, &ClickableLabel::clicked, this, [this, clusterLabel]() { + + + int current = _clusterCountSortingType.getCurrentIndex(); + int newIndex; + if (current == 0) + { + newIndex = 1; + } + else if (current == 1) + { + + if (!_customOrderClustersFromHierarchy.empty()) + { + newIndex = 2; + } + else + { + newIndex = 0; + } + } + else + { + newIndex = 0; + } + _clusterCountSortingType.setCurrentIndex(newIndex); + }); + + _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); + } + + auto legendViewFactory = mv::plugins().getPluginFactory("ChartLegend View"); + if (legendViewFactory) + { + for (auto legendPlugin : mv::plugins().getPluginsByFactory(legendViewFactory)) + { + if (legendPlugin->getGuiName() == "Legend View") + { + auto selectionColor = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Selection color")); + if (selectionColor) + { + selectionColor->setColor(QColor(53, 126, 199)); + } + auto selectionStringDelimiter = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Delimiter")); + if (selectionStringDelimiter) + { + selectionStringDelimiter->setString(","); + } + auto selectionClustersString = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster Selection string")); + if (selectionClustersString) + { + selectionClustersString->setString(selectedClustersString); + } + } + } + } + + /* + + for (auto cluster : clusterValues) { + auto clusterName = cluster.getName(); + auto clusterIndicesSize = cluster.getIndices().size(); + auto clusterColor = cluster.getColor(); // Assuming getColor() returns a QColor + + // Calculate luminance + qreal luminance = 0.299 * clusterColor.redF() + 0.587 * clusterColor.greenF() + 0.114 * clusterColor.blueF(); + + // Choose text color based on luminance + QString textColor = (luminance > 0.5) ? "black" : "white"; + + // Convert QColor to hex string for stylesheet + QString backgroundColor = clusterColor.name(QColor::HexArgb); + + auto clusterLabel = new QLabel(QString("%1: %2").arg(clusterName).arg(clusterIndicesSize)); + // Add text color and background color to clusterLabel with padding and border for better styling + clusterLabel->setStyleSheet(QString("QLabel { color: %1; background-color: %2; padding: 2px; border: 0.5px solid %3; }") + .arg(textColor).arg(backgroundColor).arg(textColor)); + _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); + } + */ + + } + + } +} + + +void SettingsAction::setModifiedTriggeredData(QVariant geneListTable) +{ + if (!geneListTable.isNull()) + { + ////startCodeTimer("Part15"); + //_filteredGeneNamesVariant.setVariant(geneListTable); + _listModel.setVariant(geneListTable); + ////stopCodeTimer("Part15"); + + } + else + { + qDebug() << "QVariant empty"; + } +} + +void createTreeInitial(QJsonObject& node, const std::map& utilityMap) { + // Check if the "name" key exists in the current node + if (node.contains("name")) { + QString nodeName = node["name"].toString(); + auto it = utilityMap.find(nodeName); + + if (it != utilityMap.end()) { + node["mean"] = std::round(it->second.meanVal * 100.0) / 100.0; // Round to 2 decimal places + node["differential"] = std::round(it->second.differentialVal * 100.0) / 100.0; // Round to 2 decimal places + + + float topAbundance = 0.0; + if (it->second.abundanceTop != 0) + { + + topAbundance = (static_cast(it->second.countAbundanceNumerator) / static_cast(it->second.abundanceTop)) * 100; + } + + float middleAbundance = 0.0; + if (it->second.abundanceMiddle != 0) + { + + middleAbundance = (static_cast(it->second.countAbundanceNumerator) / static_cast(it->second.abundanceMiddle)) * 100; + } + + + node["abundanceTop"] = topAbundance; + node["abundanceMiddle"] = middleAbundance; + node["rank"] = it->second.rankVal; + node["gene"] = it->second.geneName; + } + } + + // If the node has "children", recursively update them as well + if (node.contains("children")) { + QJsonArray children = node["children"].toArray(); + for (int i = 0; i < children.size(); ++i) { + QJsonObject child = children[i].toObject(); + createTreeInitial(child, utilityMap); // Recursive call + children[i] = child; // Update the modified object back into the array + } + node["children"] = children; // Update the modified array back into the parent JSON object + } +} + + +void SettingsAction::precomputeTreesFromHierarchy() +{ + if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) + { + return; + } + _precomputedTreesFromTheHierarchy.clear(); + auto start = std::chrono::high_resolution_clock::now(); + qDebug() << "Computing precomputeTreesFromHierarchy"; + + if (!_speciesNamesDataset.getCurrentDataset().isValid() || !_mainPointsDataset.getCurrentDataset().isValid() || !_topClusterNamesDataset.getCurrentDataset().isValid() || !_middleClusterNamesDataset.getCurrentDataset().isValid() || !_bottomClusterNamesDataset.getCurrentDataset().isValid() || !_referenceTreeDataset.getCurrentDataset().isValid()) { + qDebug() << "Datasets are not valid"; + return; + } + auto speciesNamesDataset = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); + auto mainPointsDataset = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); + auto topClusterNamesDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); + auto middleClusterNamesDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); + auto bottomClusterNamesDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); + + auto referenceTreeDataset = mv::data().getDataset(_referenceTreeDataset.getCurrentDataset().getDatasetId()); + QJsonObject speciesDataJson = referenceTreeDataset->getTreeData(); + QStringList speciesNamesVerify = referenceTreeDataset->getTreeLeafNames(); + if (speciesDataJson.isEmpty() || speciesNamesVerify.isEmpty()) + { + qDebug() << "Reference tree data is empty"; + return; + } + + + if (speciesNamesDataset.isValid() && mainPointsDataset.isValid() && topClusterNamesDataset.isValid() && middleClusterNamesDataset.isValid() && bottomClusterNamesDataset.isValid()) + { + auto speciesClusters = speciesNamesDataset->getClusters(); + + QStringList speciesClusterNames; + speciesClusterNames.reserve(speciesClusters.size()); + for (const auto& speciesCluster : speciesClusters) { + speciesClusterNames.push_back(speciesCluster.getName()); + } + if (!areSameIgnoreOrder(speciesNamesVerify, speciesClusterNames)) + { + qDebug() << "Species names do not match"; + return; + } + + + auto mainPointDimensionNames = mainPointsDataset->getDimensionNames(); + std::vector allGeneIndices(mainPointDimensionNames.size()); + std::iota(allGeneIndices.begin(), allGeneIndices.end(), 0); + + QVector topClusters = topClusterNamesDataset->getClusters(); + QVector middleClusters = middleClusterNamesDataset->getClusters(); + QVector bottomClusters = bottomClusterNamesDataset->getClusters(); + + if (!mainPointDimensionNames.empty()) { + std::map> combinedClusters = { + {"top", topClusters}, + {"middle", middleClusters}, + {"bottom", bottomClusters} + }; + + QtConcurrent::blockingMap(combinedClusters, [&](const auto& pair) { + const auto& hierarchyType = pair.first; + const auto& clusters = pair.second; + + for (const auto& cluster : clusters) { + const auto& clusterName = cluster.getName(); + auto clusterIndices = cluster.getIndices(); + std::sort(clusterIndices.begin(), clusterIndices.end()); + std::map> topSpeciesToGeneExpressionMap; + + for (const auto& species : speciesClusters) { + const auto& speciesName = species.getName(); + auto speciesIndices = species.getIndices(); + std::sort(speciesIndices.begin(), speciesIndices.end()); + + std::vector commonPointsIndices = intersectSortedIndicesToInt(speciesIndices, clusterIndices); + + if (commonPointsIndices.empty()) { + continue; + } + + const auto speciesMeanMapIt = _clusterGeneMeanExpressionMap.find(speciesName); + if (speciesMeanMapIt == _clusterGeneMeanExpressionMap.end()) { + continue; + } + const auto& speciesMeanMap = speciesMeanMapIt->second; + + std::vector selectedGeneMeans(mainPointDimensionNames.size(), 0.0f); + computeChunkedGeneMeans(mainPointsDataset, allGeneIndices, commonPointsIndices, [&](int, const std::vector& geneChunkIndices, const std::vector& chunkMeans) { + for (int localGeneIndex = 0; localGeneIndex < static_cast(geneChunkIndices.size()); ++localGeneIndex) { + selectedGeneMeans[geneChunkIndices[localGeneIndex]] = chunkMeans[localGeneIndex]; + } + }); + + const int selectedCellCount = static_cast(commonPointsIndices.size()); + const int topHierarchyCountValue = (_clusterSpeciesFrequencyMap.find(speciesName) != _clusterSpeciesFrequencyMap.end()) ? _clusterSpeciesFrequencyMap[speciesName]["topCells"] : 0; + const int middleHierarchyCountValue = (_clusterSpeciesFrequencyMap.find(speciesName) != _clusterSpeciesFrequencyMap.end()) ? _clusterSpeciesFrequencyMap[speciesName]["middleCells"] : 0; + + auto& geneStatsForSpecies = topSpeciesToGeneExpressionMap[speciesName]; + for (int geneIndex = 0; geneIndex < static_cast(mainPointDimensionNames.size()); ++geneIndex) { + const auto& geneName = mainPointDimensionNames[geneIndex]; + const auto nonSelectionDetailsIt = speciesMeanMap.find(geneName); + if (nonSelectionDetailsIt == speciesMeanMap.end()) { + continue; + } + + const int allCellCounts = nonSelectionDetailsIt->second.first; + const float allCellMean = nonSelectionDetailsIt->second.second; + const float selectedMean = selectedGeneMeans[geneIndex]; + const float allCellTotal = allCellMean * allCellCounts; + const int nonSelectedCells = allCellCounts - selectedCellCount; + const float nonSelectedMean = (nonSelectedCells > 0) ? (allCellTotal - selectedMean * selectedCellCount) / nonSelectedCells : 0.0f; + + StatisticsSingle calculateStatisticsShort = { selectedMean, selectedCellCount }; + StatisticsSingle calculateStatisticsNot = { nonSelectedMean, nonSelectedCells }; + geneStatsForSpecies[geneName] = combineStatisticsSingle(calculateStatisticsShort, calculateStatisticsNot, topHierarchyCountValue, middleHierarchyCountValue, middleHierarchyCountValue); + } + } + + enum class SelectionOption { + AbsoluteTopN, + PositiveTopN, + NegativeTopN + }; + + auto optionValue = _typeofTopNGenes.getCurrentText(); + SelectionOption option = SelectionOption::AbsoluteTopN; + if (optionValue == "Positive") { + option = SelectionOption::PositiveTopN; + } + else if (optionValue == "Negative") { + option = SelectionOption::NegativeTopN; + } + + std::map>> rankingMap; + + for (const auto& [speciesName, geneMap] : topSpeciesToGeneExpressionMap) { + std::vector> geneExpressionVec; + geneExpressionVec.reserve(geneMap.size()); + for (const auto& [geneName, stats] : geneMap) { + float differenceMeanValue = stats.meanSelected - stats.meanNonSelected; + geneExpressionVec.emplace_back(geneName, differenceMeanValue); + } + + if (option == SelectionOption::AbsoluteTopN) { + std::sort(geneExpressionVec.begin(), geneExpressionVec.end(), [](const auto& a, const auto& b) { + return std::abs(a.second) > std::abs(b.second); + }); + } + else { + std::sort(geneExpressionVec.begin(), geneExpressionVec.end(), [](const auto& a, const auto& b) { + return a.second > b.second; + }); + if (option == SelectionOption::NegativeTopN) { + std::reverse(geneExpressionVec.begin(), geneExpressionVec.end()); + } + } + + for (int i = 0; i < geneExpressionVec.size(); ++i) { + int rank = (option == SelectionOption::NegativeTopN) ? geneExpressionVec.size() - i : i + 1; + rankingMap[geneExpressionVec[i].first].emplace_back(speciesName, rank); + } + } + + for (auto& [geneName, speciesRankVec] : rankingMap) { + std::map utilityMap; + for (const auto& [speciesName, rank] : speciesRankVec) { + InitialStatistics tempStats; + tempStats.rankVal = rank; + tempStats.geneName = geneName; + tempStats.meanVal = topSpeciesToGeneExpressionMap[speciesName][geneName].meanSelected; + tempStats.differentialVal = topSpeciesToGeneExpressionMap[speciesName][geneName].meanSelected - topSpeciesToGeneExpressionMap[speciesName][geneName].meanNonSelected; + + tempStats.abundanceTop = (topSpeciesToGeneExpressionMap[speciesName][geneName].abundanceTop != 0) ? topSpeciesToGeneExpressionMap[speciesName][geneName].meanSelected / topSpeciesToGeneExpressionMap[speciesName][geneName].abundanceTop : 0.0f; + + tempStats.abundanceMiddle = (topSpeciesToGeneExpressionMap[speciesName][geneName].abundanceMiddle != 0) ? topSpeciesToGeneExpressionMap[speciesName][geneName].meanSelected / topSpeciesToGeneExpressionMap[speciesName][geneName].abundanceMiddle : 0.0f; + + utilityMap[speciesName] = tempStats; + } + + createTreeInitial(speciesDataJson, utilityMap); + + //convert QJsonObjectToString to store in a more space efficientway and then again convert the string to QJSONObject + QString jsonString = QJsonDocument(speciesDataJson).toJson(QJsonDocument::Compact); + + + _precomputedTreesFromTheHierarchy[hierarchyType][clusterName][geneName] = jsonString; + } + } + }); + + } + + else + { + qDebug() << "Datasets are not valid"; + return; + } + + } + else + { + qDebug() << "Datasets are not valid"; + return; + } + + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start).count(); + qDebug() << "Time taken for precomputeTreesFromHierarchy : " + QString::number(duration / 1000.0) + " s"; + //_popupMessageInit.hide(); + //_popupMessageTreeCreationCompletion->show(); + //QApplication::processEvents(); +} + + +void SettingsAction::computeGeneMeanExpressionMap() +{ + if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) + { + return; + } + + + _clusterGeneMeanExpressionMap.clear(); + auto start = std::chrono::high_resolution_clock::now(); + qDebug() << "Computing gene mean expression map"; + + _clusterGeneMeanExpressionMap.clear(); + if (_speciesNamesDataset.getCurrentDataset().isValid() && _mainPointsDataset.getCurrentDataset().isValid()) { + auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); + auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); + if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) { + auto speciesclusters = speciesClusterDatasetFull->getClusters(); + auto mainPointDimensionNames = mainPointDatasetFull->getDimensionNames(); + std::vector allGeneIndices(mainPointDimensionNames.size()); + std::iota(allGeneIndices.begin(), allGeneIndices.end(), 0); + QMutex mapMutex; + + QtConcurrent::blockingMap(speciesclusters, [&](const auto& species) { + auto speciesIndices = species.getIndices(); + auto speciesName = species.getName(); + std::unordered_map> localGeneMeans; + localGeneMeans.reserve(mainPointDimensionNames.size()); + + computeChunkedGeneMeans(mainPointDatasetFull, allGeneIndices, speciesIndices, [&](int, const std::vector& geneChunkIndices, const std::vector& chunkMeans) { + for (int localGeneIndex = 0; localGeneIndex < static_cast(geneChunkIndices.size()); ++localGeneIndex) { + const int geneIndex = geneChunkIndices[localGeneIndex]; + localGeneMeans[mainPointDimensionNames[geneIndex]] = std::make_pair(static_cast(speciesIndices.size()), chunkMeans[localGeneIndex]); + } + }); + + QMutexLocker locker(&mapMutex); + _clusterGeneMeanExpressionMap[speciesName] = std::move(localGeneMeans); + }); + + _meanMapComputed = true; + } + } + + + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start).count(); + qDebug() << "Time taken for computeGeneMeanExpressionMap : " + QString::number(duration / 1000.0) + " s"; +} + +void SettingsAction::computeHierarchyAppearanceVector() +{ + if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) + { + return; + } + + auto startTimer = std::chrono::high_resolution_clock::now(); + qDebug() << "computeHierarchyAppearanceVector Start"; + _topHierarchyClusterMap.clear(); + + if (_mainPointsDataset.getCurrentDataset().isValid()) { + auto fullMainDataset = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); + auto numOfPoints = fullMainDataset->getNumPoints(); + auto clusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); + QStringList inclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + const auto inclusionSet = toStringSet(inclusionList); + if (clusterDataset.isValid()) { + auto clusters = clusterDataset->getClusters(); + if (!clusters.empty()) { + + for (const auto& cluster : clusters) { + + auto clusterName = cluster.getName(); + if (inclusionSet.find(clusterName) != inclusionSet.end()) + { + std::vector clusterNamesAppearance(numOfPoints, false); + for (const auto& index : cluster.getIndices()) { + clusterNamesAppearance[index] = true; + } + _topHierarchyClusterMap[clusterName] = clusterNamesAppearance; + } + + + } + } + } + + } + + auto endTimer = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(endTimer - startTimer).count(); + qDebug() << "Time taken for computeHierarchyAppearanceVector : " + QString::number(duration / 1000.0) + " s"; + +} + + +void SettingsAction::computeFrequencyMapForHierarchyItemsChange(QString hierarchyType) +{ + if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked() || hierarchyType.isEmpty()) + { + return; + } + + auto startTimer = std::chrono::high_resolution_clock::now(); + qDebug() << "computeFrequencyMapForHierarchyItemsChange Start for " + hierarchyType; + + if (!_speciesNamesDataset.getCurrentDataset().isValid() || !_mainPointsDataset.getCurrentDataset().isValid()) { + return; + } + + auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); + auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); + auto numOfPoints = mainPointDatasetFull->getNumPoints(); + std::vector clusterNames(numOfPoints, true); + QStringList inclusionList; + mv::Dataset clusterDataset; + + if (hierarchyType == "top" && _topClusterNamesDataset.getCurrentDataset().isValid()) + { + inclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + clusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); + } + /* + else if (hierarchyType == "middle" && _middleClusterNamesDataset.getCurrentDataset().isValid()) + { + inclusionList = _middleHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + clusterDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); + } + else if (hierarchyType == "bottom" && _bottomClusterNamesDataset.getCurrentDataset().isValid()) + { + inclusionList = _bottomHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + clusterDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); + } + */ + const auto inclusionSet = toStringSet(inclusionList); + if (clusterDataset.isValid()) + { + for (const auto& cluster : clusterDataset->getClusters()) + { + if (inclusionSet.find(cluster.getName()) == inclusionSet.end()) + { + for (const auto& index : cluster.getIndices()) + { + clusterNames[index] = false; + } + } + } + } + + if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) + { + auto speciesclusters = speciesClusterDatasetFull->getClusters(); + for (const auto& species : speciesclusters) { + auto speciesIndices = species.getIndices(); + auto speciesName = species.getName(); + int count = std::count_if(speciesIndices.begin(), speciesIndices.end(), [&clusterNames](int index) { + return clusterNames[index]; + }); + + if (hierarchyType == "top") + { + _clusterSpeciesFrequencyMap[speciesName]["topCells"] = count; + } + else if (hierarchyType == "middle") + { + _clusterSpeciesFrequencyMap[speciesName]["middleCells"] = count; + } + else if (hierarchyType == "bottom") + { + _clusterSpeciesFrequencyMap[speciesName]["bottomCells"] = count; + } + } + } + + auto endTimer = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(endTimer - startTimer).count(); + qDebug() << "Time taken for computeFrequencyMapForHierarchyItemsChange for " + hierarchyType + " : " + QString::number(duration / 1000.0) + " s"; +} +/* +void SettingsAction::computeGeneMeanExpressionMapForHierarchyItemsChangeExperimental(QString hierarchyType) +{ + if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) + { + return; + } + auto startTimer = std::chrono::high_resolution_clock::now(); + qDebug() << "computeGeneMeanExpressionMapForHierarchyItemsChange Experimental Start for " + hierarchyType; + if (hierarchyType == "") + { + return; + } + + + if (_speciesNamesDataset.getCurrentDataset().isValid() && _mainPointsDataset.getCurrentDataset().isValid()) { + + auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); + auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); + auto numOfPoints = mainPointDatasetFull->getNumPoints(); + std::vector clusterNames(numOfPoints, true); + bool datasetCheck = false; + QStringList inclusionList; + if (hierarchyType == "top") + { + inclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + if (_topClusterNamesDataset.getCurrentDataset().isValid()) + { + datasetCheck = true; + } + } + else if (hierarchyType == "middle") + { + inclusionList = _middleHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + if (_middleClusterNamesDataset.getCurrentDataset().isValid()) + { + datasetCheck = true; + } + } + else if (hierarchyType == "bottom") + { + inclusionList = _bottomHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + if (_bottomClusterNamesDataset.getCurrentDataset().isValid()) + { + datasetCheck = true; + } + } + + if (datasetCheck) + { + mv::Dataset clusterDataset; + + if (hierarchyType == "top") + { + clusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); + } + else if (hierarchyType == "middle") + { + clusterDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); + } + else if (hierarchyType == "bottom") + { + clusterDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); + } + + for (auto cluster : clusterDataset->getClusters()) + { + auto clusterIndices = cluster.getIndices(); + auto clusterName = cluster.getName(); + if (!inclusionList.contains(clusterName)) + { + for (auto index : clusterIndices) + { + clusterNames[index] = false; + } + } + + } + } + + + + if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) + { + auto speciesclusters = speciesClusterDatasetFull->getClusters(); + auto mainPointDimensionNames = mainPointDatasetFull->getDimensionNames(); + for (auto species : speciesclusters) { + auto speciesIndices = species.getIndices(); + auto speciesName = species.getName(); + std::vector indices; + + // Loop through all species indices to determine if they are in respective clusters + for (int i = 0; i < speciesIndices.size(); ++i) { + // Check if the current species index is present in the cluster and only include those that are true + if (std::find(clusterNames.begin(), clusterNames.end(), speciesIndices[i]) != clusterNames.end()) { + indices.push_back(i); + } + + } + + for (int i = 0; i < mainPointDimensionNames.size(); i++) { + auto& geneName = mainPointDimensionNames[i]; + auto geneIndex = { i }; + + + + std::vector resultContainer(indices.size()); + mainPointDatasetFull->populateDataForDimensions(resultContainer, geneIndex, indices); + float topMean = calculateMean(resultContainer); + + if (hierarchyType == "top") + { + _clusterGeneMeanExpressionMap[speciesName][geneName]["topCells"] = std::make_pair(indices.size(), topMean); + } + else if (hierarchyType == "middle") + { + _clusterGeneMeanExpressionMap[speciesName][geneName]["middleCells"] = std::make_pair(indices.size(), topMean); + } + else if (hierarchyType == "bottom") + { + _clusterGeneMeanExpressionMap[speciesName][geneName]["bottomCells"] = std::make_pair(indices.size(), topMean); + } + } + + } + + + } + } + auto endTimer = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(endTimer - startTimer).count(); + qDebug() << "Time taken for computeGeneMeanExpressionMapForHierarchyItemsChangeExperimental for " + hierarchyType + " : " + QString::number(duration / 1000.0) + " s"; + +} +void SettingsAction::computeGeneMeanExpressionMapExperimental() +{ + if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) + { + return; + } + auto start = std::chrono::high_resolution_clock::now(); + qDebug() << "Computing gene mean expression map"; + + + _clusterGeneMeanExpressionMap.clear(); + + if (_speciesNamesDataset.getCurrentDataset().isValid() && _mainPointsDataset.getCurrentDataset().isValid()) { + + auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); + auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); + auto numOfPoints = mainPointDatasetFull->getNumPoints(); + std::vector topClusterNames(numOfPoints, true); + std::vector middleClusterNames(numOfPoints, true); + std::vector bottomClusterNames(numOfPoints, true); + QStringList topInclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + QStringList middleInclusionList = _middleHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + QStringList bottomInclusionList = _bottomHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); + if (_topClusterNamesDataset.getCurrentDataset().isValid() && _middleClusterNamesDataset.getCurrentDataset().isValid() && _bottomClusterNamesDataset.getCurrentDataset().isValid()) + + { + auto topClusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); + auto middleClusterDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); + auto bottomClusterDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); + + auto processCluster = [&](const Clusters& dataset, std::vector& clusterNames) { + for (const auto& cluster : dataset.getClusters()) { + auto clusterIndices = cluster.getIndices(); + auto clusterName = cluster.getName(); + if (!topInclusionList.contains(clusterName)) { + for (auto index : clusterIndices) { + if (index < clusterNames.size()) { + clusterNames[index] = false; + } + } + } + } + }; + + QFuture topFuture = QtConcurrent::run([&]() { processCluster(*topClusterDataset, topClusterNames); }); + QFuture middleFuture = QtConcurrent::run([&]() { processCluster(*middleClusterDataset, middleClusterNames); }); + QFuture bottomFuture = QtConcurrent::run([&]() { processCluster(*bottomClusterDataset, bottomClusterNames); }); + + topFuture.waitForFinished(); + middleFuture.waitForFinished(); + bottomFuture.waitForFinished(); + } + + + + // Ensure that the types match + QMutex mapMutex; // Mutex to protect shared access to _clusterGeneMeanExpressionMap + + if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) { + auto speciesclusters = speciesClusterDatasetFull->getClusters(); + auto mainPointDimensionNames = mainPointDatasetFull->getDimensionNames(); + + // Parallel processing of species clusters + QtConcurrent::blockingMap(speciesclusters, [&](const auto& species) { + auto speciesIndices = species.getIndices(); + auto speciesName = species.getName(); + + std::vector topIndices; + std::vector middleIndices; + std::vector bottomIndices; + + // Determine cluster membership for the species + for (uint32_t i = 0; i < speciesIndices.size(); ++i) { + if (std::binary_search(topClusterNames.begin(), topClusterNames.end(), speciesIndices[i])) { + topIndices.push_back(i); + } + if (std::binary_search(middleClusterNames.begin(), middleClusterNames.end(), speciesIndices[i])) { + middleIndices.push_back(i); + } + if (std::binary_search(bottomClusterNames.begin(), bottomClusterNames.end(), speciesIndices[i])) { + bottomIndices.push_back(i); + } + } + + // Parallel processing of gene expressions within each species + QtConcurrent::blockingMap(mainPointDimensionNames, [&](const auto& geneName) { + // Manually find the index of the geneName + int geneIndex = std::distance(mainPointDimensionNames.begin(), + std::find(mainPointDimensionNames.begin(), mainPointDimensionNames.end(), geneName)); + + if (geneIndex == mainPointDimensionNames.size()) { + // Handle case where geneName is not found if necessary + return; // Skip processing if the index is invalid + } + + auto processCells = [&](const std::vector& indices, const QString& cellType) { + std::vector resultContainer(indices.size()); + mainPointDatasetFull->populateDataForDimensions(resultContainer, std::vector{geneIndex}, indices); + float mean = calculateMean(resultContainer); + QMutexLocker locker(&mapMutex); + _clusterGeneMeanExpressionMap[speciesName][geneName][cellType] = std::make_pair(indices.size(), mean); + }; + + processCells(speciesIndices, "allCells"); + processCells(topIndices, "topCells"); + processCells(middleIndices, "middleCells"); + processCells(bottomIndices, "bottomCells"); + }); + }); + + _meanMapComputed = true; + } + + + + } + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start).count(); + qDebug() << "\n\n++++++++++++++++++Time taken for computeGeneMeanExpressionMap : " + QString::number(duration / 1000.0) + " s"; + +} +*/ +void SettingsAction::findTopNGenesPerCluster() { + + int n = _topNGenesFilter.getValue(); + + if (_clusterNameToGeneNameToExpressionValue.empty() || n <= 0) { + return; + } + + // startCodeTimer("findTopNGenesPerCluster"); + + enum class SelectionOption { + AbsoluteTopN, + PositiveTopN, + NegativeTopN + }; + auto optionValue = _typeofTopNGenes.getCurrentText(); + SelectionOption option = SelectionOption::AbsoluteTopN; + if (optionValue == "Positive") { + option = SelectionOption::PositiveTopN; + } + else if (optionValue == "Negative") { + option = SelectionOption::NegativeTopN; + } + + _uniqueReturnGeneList.clear(); + std::map> geneAppearanceCounter; + std::map>> rankingMap; + std::vector speciesOrder; + speciesOrder.reserve(_clusterNameToGeneNameToExpressionValue.size()); + + for (const auto& outerPair : _clusterNameToGeneNameToExpressionValue) { + auto speciesName = outerPair.first; + speciesOrder.push_back(speciesName); + struct GeneRankingEntry { + QString geneName; + float differenceMeanValue; + float meanSelected; + }; + + std::vector geneExpressionVec; + geneExpressionVec.reserve(outerPair.second.size()); + for (const auto& innerPair : outerPair.second) { + const auto& geneName = innerPair.first; + const auto& stats = innerPair.second; + geneExpressionVec.push_back({ geneName, stats.meanSelected - stats.meanNonSelected, stats.meanSelected }); + } + + if (option == SelectionOption::AbsoluteTopN) { + std::sort(geneExpressionVec.begin(), geneExpressionVec.end(), [](const auto& a, const auto& b) { + return std::abs(a.differenceMeanValue) > std::abs(b.differenceMeanValue); + }); + } + else { + std::sort(geneExpressionVec.begin(), geneExpressionVec.end(), [](const auto& a, const auto& b) { + return a.differenceMeanValue > b.differenceMeanValue; + }); + if (option == SelectionOption::NegativeTopN) { + std::reverse(geneExpressionVec.begin(), geneExpressionVec.end()); + } + } + + for (int i = 0; i < static_cast(geneExpressionVec.size()); ++i) { + const auto& entry = geneExpressionVec[i]; + if (i < n) { + _uniqueReturnGeneList.insert(entry.geneName); + if (entry.meanSelected > 0) { + geneAppearanceCounter[entry.geneName].push_back(speciesName); + } + } + const int rank = (option == SelectionOption::NegativeTopN) ? static_cast(geneExpressionVec.size()) - i : i + 1; + rankingMap[entry.geneName].emplace_back(speciesName, rank); + } + } + + //iterate std::map>> rankingMap; + // Iterating over the map + if (_performGeneTableTsneAction.isChecked()) { + std::vector rankOrder; + _geneOrder.clear(); + _geneOrder.reserve(rankingMap.size()); + for (const auto& item : rankingMap) { + _geneOrder.push_back(item.first); + } + rankOrder.resize(_geneOrder.size() * speciesOrder.size(), 0.0f); // Initialize with 0.0f for clarity + + std::unordered_map speciesIndexMap; + speciesIndexMap.reserve(speciesOrder.size()); + for (int i = 0; i < static_cast(speciesOrder.size()); ++i) { + speciesIndexMap[speciesOrder[i]] = i; + } + + for (int geneIndex = 0; geneIndex < _geneOrder.size(); ++geneIndex) { + const QString& gene = _geneOrder[geneIndex]; + const auto& speciesRanks = rankingMap[gene]; + for (const auto& speciesRank : speciesRanks) { + const QString& species = speciesRank.first; + const float rank = (speciesRank.second <= n) ? 1.0f : 0.0f; + int speciesIndex = speciesIndexMap[species]; + rankOrder[geneIndex * speciesOrder.size() + speciesIndex] = rank; + } + } + + QString pointDataId = _geneSimilarityPoints->getId(); + int pointDimSize = static_cast(speciesOrder.size()); + int pointIndicesSize = static_cast(_geneOrder.size()); + + if (_selectedPointsTSNEDatasetForGeneTable.isValid()) + { + auto runningAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Running")); + + if (runningAction) + { + + if (runningAction->isChecked()) + { + auto stopAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Stop")); + if (stopAction) + { + stopAction->trigger(); + //std::this_thread::sleep_for(std::chrono::seconds(5)); + } + } + + } + mv::data().removeDataset(_selectedPointsTSNEDatasetForGeneTable); + } + + populatePointData(pointDataId, rankOrder, pointIndicesSize, pointDimSize, speciesOrder); + + mv::plugin::AnalysisPlugin* analysisPlugin; + auto scatterplotModificationsGeneSimilarity = [this]() { + if (_selectedPointsTSNEDatasetForGeneTable.isValid()) { + auto scatterplotViewFactory = mv::plugins().getPluginFactory("Scatterplot View"); + mv::gui::DatasetPickerAction* colorDatasetPickerAction; + mv::gui::DatasetPickerAction* pointDatasetPickerAction; + mv::gui::ViewPluginSamplerAction* samplerActionAction; + if (scatterplotViewFactory) { + for (auto plugin : mv::plugins().getPluginsByFactory(scatterplotViewFactory)) { + if (plugin->getGuiName() == "Scatterplot Cell Selection Overview") { + pointDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Position")); + if (pointDatasetPickerAction) { + pointDatasetPickerAction->setCurrentText(""); + + pointDatasetPickerAction->setCurrentDataset(_selectedPointsTSNEDatasetForGeneTable); + + colorDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Color")); + if (colorDatasetPickerAction) + { + colorDatasetPickerAction->setCurrentText(""); + + if (_geneSimilarityClusterColoring.isValid()) + { + colorDatasetPickerAction->setCurrentDataset(_geneSimilarityClusterColoring); + auto legendViewFactory = mv::plugins().getPluginFactory("ChartLegend View"); + if (legendViewFactory) + { + for (auto legendPlugin : mv::plugins().getPluginsByFactory(legendViewFactory)) + { + if (legendPlugin->getGuiName() == "Legend View") + { + //legendPlugin->printChildren(); + auto legendDatasetPickerAction = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster dataset")); + if (legendDatasetPickerAction) + { + legendDatasetPickerAction->setCurrentDataset(_geneSimilarityClusterColoring); + } + auto chartTitle = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Chart Title")); + if (chartTitle) + { + chartTitle->setString("Cell types"); + } + /* + auto selectionColor = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Selection color")); + if (selectionColor) + { + selectionColor->setColor(QColor(53, 126, 199)); + } + auto selectionStringDelimiter = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Delimiter")); + if (selectionStringDelimiter) + { + selectionStringDelimiter->setString(","); + } + + auto selectionClustersString = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster Selection string")); + if (selectionClustersString) + { + selectionClustersString->setString(""); //TODO + } + */ + } + } + } + + } + } + + samplerActionAction = plugin->findChildByPath("Sampler"); + + if (samplerActionAction) + { + samplerActionAction->setHtmlViewGeneratorFunction([this](const ViewPluginSamplerAction::SampleContext& toolTipContext) -> QString { + QString clusterDatasetId = _speciesNamesDataset.getCurrentDataset().getDatasetId(); + return generateTooltip(toolTipContext, clusterDatasetId, true, "GlobalPointIndices"); + }); + } + } + } + } + } + } + + }; + + + + { + //startCodeTimer("Part10"); + analysisPlugin = mv::plugins().requestPlugin("tSNE Analysis", { _geneSimilarityPoints }); + if (!analysisPlugin) { + qDebug() << "Could not find create TSNE Analysis"; + return; + } + _selectedPointsTSNEDatasetForGeneTable = analysisPlugin->getOutputDataset(); + int groupID2 = 10 * 3; + _selectedPointsTSNEDatasetForGeneTable->setGroupIndex(groupID2); + if (_selectedPointsTSNEDatasetForGeneTable.isValid()) + { + //_selectedPointsTSNEDatasetForGeneTable->printChildren(); + bool skip = false; + int perplexity = std::min(static_cast(_geneOrder.size()), _tsnePerplexity.getValue()); + if (perplexity < 5) + { + qDebug() << "Perplexity is less than 5"; + skip = true; + //_startComputationTriggerAction.setDisabled(false); + } + if (!skip) + { + if (perplexity != _tsnePerplexity.getValue()) + { + _tsnePerplexity.setValue(perplexity); + } + + auto perplexityAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/Perplexity")); + if (perplexityAction) + { + //qDebug() << "Perplexity: Found"; + perplexityAction->setValue(perplexity); + } + else + { + qDebug() << "Perplexity: Not Found"; + } + + QString knnAlgorithmValue = _performGeneTableTsneKnn.getCurrentText(); + QString distanceMetricValue = _performGeneTableTsneDistance.getCurrentText(); + if (knnAlgorithmValue != "") + { + auto knnAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/kNN Algorithm")); + if (knnAction) + { + //qDebug() << "Knn: Found"; + try { + knnAction->setCurrentText(knnAlgorithmValue); + } + catch (const std::exception& e) { + qDebug() << "An exception occurred in setting knn value: " << e.what(); + } + } + else + { + qDebug() << "Knn: Not Found"; + } + } + if (distanceMetricValue != "") + { + auto distanceAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/Distance metric")); + if (distanceAction) + { + //qDebug() << "Distance: Found"; + try { + distanceAction->setCurrentText(distanceMetricValue); + } + catch (const std::exception& e) { + qDebug() << "An exception occurred in setting distance value: " << e.what(); + } + } + else + { + qDebug() << "Distance: Not Found"; + } + } + + scatterplotModificationsGeneSimilarity(); + + auto startAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Start")); + if (startAction) { + + startAction->trigger(); + + analysisPlugin->getOutputDataset()->setSelectionIndices({}); + } + } + } + //stopCodeTimer("Part10"); + } + + std::vector selectedIndices; + std::vector nonselectedIndices; + selectedIndices.reserve(_geneOrder.size()); // Pre-allocate memory + nonselectedIndices.reserve(_geneOrder.size()); // Pre-allocate memory + for (int i = 0; i < _geneOrder.size(); i++) + { + if (_uniqueReturnGeneList.find(_geneOrder[i]) != _uniqueReturnGeneList.end()) + { + selectedIndices.push_back(i); + } + else + { + nonselectedIndices.push_back(i); + } + } + QString clusterDataId = _geneSimilarityClusterColoring->getId(); + QColor selectedColor = QColor("#00A2ED"); + QColor nonSelectedColor = QColor("#ff5d12"); + std::map>> selectedClusterMap; + selectedClusterMap["TopNSelectedGenes"] = { selectedColor, selectedIndices }; + selectedClusterMap["NonTopNGenes"] = { nonSelectedColor, nonselectedIndices }; + + populateClusterData(clusterDataId, selectedClusterMap); + } + + //stopCodeTimer("findTopNGenesPerCluster"); + QVariant returnedmodel = createModelFromData(_clusterNameToGeneNameToExpressionValue, geneAppearanceCounter, rankingMap, n); + + setModifiedTriggeredData(returnedmodel); + _selectedGene.setString(""); + //return returnedmodel; +} + diff --git a/src/SettingsAction.Data.inl b/src/SettingsAction.Data.inl new file mode 100644 index 0000000..a18b46b --- /dev/null +++ b/src/SettingsAction.Data.inl @@ -0,0 +1,749 @@ +void SettingsAction::clearTemporaryDatasetHandles() +{ + _selectedPointsTSNEDataset = Dataset(); + _selectedPointsDataset = Dataset(); + _selectedPointsEmbeddingDataset = Dataset(); + _filteredUMAPDatasetPoints = Dataset(); + _filteredUMAPDatasetColors = Dataset(); + _filteredUMAPDatasetClusters = Dataset(); + _tsneDatasetExpressionColors = Dataset(); + _geneSimilarityPoints = Dataset(); + + _tsneDatasetSpeciesColors = Dataset(); + _tsneDatasetClusterColors = Dataset(); + _geneSimilarityClusterColoring = Dataset(); +} + +void SettingsAction::removeDatasets(int groupId) +{ + auto allDatasets = mv::data().getAllDatasets(); + + // id -> dataset pointer (NO COPYING) + QHash> idToDataset; + idToDataset.reserve(allDatasets.size()); + + for (const auto& ds : allDatasets) { + if (ds->getGroupIndex() == groupId) { + idToDataset.insert(ds->getId(), ds); + } + } + + // Cache depth (memoization) + QHash depthCache; + depthCache.reserve(idToDataset.size()); + + std::function depthOf = + [&](const QString& id) -> int + { + auto it = depthCache.find(id); + if (it != depthCache.end()) + return it.value(); + + int depth = 0; + + auto ds = idToDataset.value(id); + auto parent = ds->getParent(); + + if (parent.isValid()) { + QString parentId = parent->getId(); + + if (idToDataset.contains(parentId)) { + depth = 1 + depthOf(parentId); + } + } + + depthCache.insert(id, depth); + return depth; + }; + + // Build list of ids + QVector ids; + ids.reserve(idToDataset.size()); + + for (auto it = idToDataset.begin(); it != idToDataset.end(); ++it) { + ids.push_back(it.key()); + } + + // Compute all depths (O(N)) + for (const auto& id : ids) { + depthOf(id); + } + + // Sort deepest first (critical step) + std::sort(ids.begin(), ids.end(), + [&](const QString& a, const QString& b) { + return depthCache[a] > depthCache[b]; + }); + + // Delete in correct order + for (const auto& id : ids) { + auto ds = idToDataset.value(id); + if (ds.isValid()) { + qDebug() << "Deleting:" << ds->getId() << "with name:" << ds->getGuiName() + << "depth:" << depthCache[id]; + + mv::data().removeDataset(ds); + } + } +} +QVariant SettingsAction::createModelFromData(const std::map>& map, const std::map>& geneCounter, const std::map>>& rankingMap, const int& n) { + + if (map.empty() || _totalGeneList.empty()) { + return QVariant(); + } + //startCodeTimer("createModelFromData"); + QStandardItemModel* model = new QStandardItemModel(); + _initColumnNames = { "ID", "Species \nAppearance", "Gene Appearance Species Names", "Statistics" }; + model->setColumnCount(_initColumnNames.size()); + model->setRowCount(static_cast(_totalGeneList.size())); + model->setHorizontalHeaderLabels(_initColumnNames); + + QStringList headers = _initColumnNames; + _hiddenShowncolumns.setOptions(headers); + _hiddenShowncolumns.setSelectedOptions({ headers[0], headers[1] }); + + // Pre-index the nested species map once so row construction does not scan + // every species for every gene. + QHash>> geneToSpeciesStats; + geneToSpeciesStats.reserve(static_cast(_totalGeneList.size())); + for (const auto& [speciesName, innerMap] : map) { + for (const auto& [geneName, stats] : innerMap) { + geneToSpeciesStats[geneName].append(qMakePair(speciesName, stats)); + } + } + + QHash> geneToSpeciesRank; + geneToSpeciesRank.reserve(static_cast(rankingMap.size())); + for (const auto& [geneName, speciesRanks] : rankingMap) { + auto& rankMap = geneToSpeciesRank[geneName]; + rankMap.reserve(static_cast(speciesRanks.size())); + for (const auto& [speciesName, rank] : speciesRanks) { + rankMap.insert(speciesName, rank); + } + } + + for (int rowIndex = 0; rowIndex < static_cast(_totalGeneList.size()); ++rowIndex) { + const auto& gene = _totalGeneList[rowIndex]; + const auto statisticsValuesForSpecies = geneToSpeciesStats.value(gene); + const auto rankcounter = geneToSpeciesRank.value(gene); + + model->setItem(rowIndex, 0, new QStandardItem(gene)); // ID(string) should sort by string + + QString speciesGeneAppearancesComb; + int count = 0; + if (auto it = geneCounter.find(gene); it != geneCounter.end()) { + const auto& speciesDetails = it->second; + count = static_cast(speciesDetails.size()); + QStringList speciesNames; + for (const auto& speciesDetail : speciesDetails) { + speciesNames << speciesDetail; + } + speciesGeneAppearancesComb = speciesNames.join(";"); + } + auto* countItem = new QStandardItem(); // Gene Appearances (int) should sort by int + countItem->setData(count, Qt::DisplayRole); + countItem->setData(count, Qt::UserRole); // Use Qt::UserRole or another custom role for sorting by integer + model->setItem(rowIndex, 1, countItem); + + //row.push_back(new QStandardItem(QString::number(count))); // Gene Appearances (int) should sort by int + model->setItem(rowIndex, 2, new QStandardItem(speciesGeneAppearancesComb)); // Gene Appearance Species Names (string) should sort by string + + QString formattedStatistics; + formattedStatistics.reserve(statisticsValuesForSpecies.size() * 96); + for (const auto& speciesStats : statisticsValuesForSpecies) { + const auto& species = speciesStats.first; + const auto& stats = speciesStats.second; + formattedStatistics += QString("Species: %1, Rank: %2, AbundanceTop: %3, AbundanceMiddle: %4, CountAbundanceNumerator: %5, MeanSelected: %6, CountSelected: %7, MeanNotSelected: %8, CountNotSelected: %9;\n")//, MeanAll: %7, CountAll: %8 + .arg(species) + .arg(rankcounter.value(species)) + .arg(stats.abundanceTop) + .arg(stats.abundanceMiddle) + .arg(stats.countAbundanceNumerator) + .arg(stats.meanSelected, 0, 'f', 2) + .arg(stats.countSelected) + .arg(stats.meanNonSelected, 0, 'f', 2) + .arg(stats.countNonSelected) + //.arg(stats.meanAll, 0, 'f', 2) + //.arg(stats.countAll) + ; + } + model->setItem(rowIndex, 3, new QStandardItem(formattedStatistics)); // Statistics (string) should sort by string + } + + //stopCodeTimer("createModelFromData"); + + return QVariant::fromValue(model); + +} +void SettingsAction::createClusterPositionMap() +{ + _clusterPositionMap; +} +QStringList SettingsAction::getSystemModeColor() { + // Get the application palette + QPalette palette = QApplication::palette(); + + // Check the color of the window text to determine if the system is in dark mode or light mode + // Assuming dark mode has lighter text (e.g., white) and light mode has darker text (e.g., black) + if (palette.color(QPalette::WindowText).lightness() < 128) { + // Light mode + return { "#FFFFFF","#000000" }; // White + } + else { + // Dark mode + return { "#000000","#FFFFFF" }; // Black + } +} + + +void SettingsAction::exportTableViewToCSVForGenes(QTableView* tableView) { + if (!tableView) { + qWarning() << "TableView is null."; + return; + } + + QAbstractItemModel* model = tableView->model(); + if (!model) { + qWarning() << "TableView model is null."; + return; + } + + QString filePath = QFileDialog::getSaveFileName(nullptr, "Save CSV", "", "CSV Files (*.csv);;All Files (*)"); + if (filePath.isEmpty()) { + qWarning() << "No file selected for saving."; + return; + } + + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << "Could not open file for writing: " << filePath; + return; + } + + QTextStream stream(&file); + + if (model->columnCount() == 4) + { + //ID,Species Appearance,Gene Appearance Species Names,Statistics + QString headerString = "ID,Species Appearance,Gene Appearance Species Names"; + stream << headerString; + stream << "\n"; + + for (int row = 0; row < model->rowCount(); ++row) { + for (int col = 0; col < model->columnCount(); ++col) + { + if(col < 3){ + if (col > 0) { + stream << ","; + } + + stream << model->data(model->index(row, col)).toString(); + } + } + stream << "\n"; + } + } + else + { + // Write header + for (int col = 0; col < model->columnCount(); ++col) { + if (col > 0) { + stream << ","; + } + stream << model->headerData(col, Qt::Horizontal).toString(); + } + stream << "\n"; + + // Write data + for (int row = 0; row < model->rowCount(); ++row) { + for (int col = 0; col < model->columnCount(); ++col) { + if (col > 0) { + stream << ","; + } + stream << model->data(model->index(row, col)).toString(); + } + stream << "\n"; + } + } + + + file.close(); +} + +QString computeMapFromStatistics(QString geneName, QStringList geneAppearanceSpeciesNamesList, QStringList statisticsList) +{ + QString finalString = ""; + + for (int i = 0; i < statisticsList.size(); i++) + { + QString tempString = statisticsList[i]; + QStringList pairs = tempString.split(", "); + + QString speciesName = ""; + QString rank = ""; + QString abundanceTop = ""; + QString abundanceMiddle = ""; + QString countAbundanceNumerator = ""; + QString meanSelected = ""; + QString countSelected = ""; + QString meanNotSelected = ""; + QString countNotSelected = ""; + + for (const QString& pair : pairs) { + QStringList keyValue = pair.split(": "); + if (keyValue.size() == 2) { + QString key = keyValue[0].trimmed(); + QString value = keyValue[1].trimmed(); + + if (key == "Species") { + speciesName = value; + } + else if (key == "Rank") { + rank = value; + } + else if (key == "AbundanceTop") { + abundanceTop = value; + } + else if (key == "AbundanceMiddle") { + abundanceMiddle = value; + } + else if (key == "CountAbundanceNumerator") { + countAbundanceNumerator = value; + } + else if (key == "MeanSelected") { + meanSelected = value; + } + else if (key == "CountSelected") { + countSelected = value; + } + else if (key == "MeanNotSelected") { + meanNotSelected = value; + } + else if (key == "CountNotSelected") { + countNotSelected = value; + } + } + } + + finalString += geneName; + if (geneAppearanceSpeciesNamesList.contains(speciesName)) { + finalString += ", True"; + } + else { + finalString += ", False"; + } + finalString += ", " + speciesName; + finalString += ", " + QString::number(meanSelected.toFloat() - meanNotSelected.toFloat()); + finalString += ", " + rank; + finalString += ", " + abundanceTop; + finalString += ", " + abundanceMiddle; + finalString += ", " + countSelected; + finalString += ", " + meanSelected; + finalString += ", " + countNotSelected; + finalString += ", " + meanNotSelected; + if (i < statisticsList.size() - 1) { + finalString += "\n"; + } + } + + return finalString; +} + + +void SettingsAction::exportTableViewToCSVPerGene(QTableView* tableView) { + if (!tableView) { + qWarning() << "TableView is null."; + return; + } + + QAbstractItemModel* model = tableView->model(); + if (!model) { + qWarning() << "TableView model is null."; + return; + } + + QString filePath = QFileDialog::getSaveFileName(nullptr, "Save CSV", "", "CSV Files (*.csv);;All Files (*)"); + if (filePath.isEmpty()) { + qWarning() << "No file selected for saving."; + return; + } + + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << "Could not open file for writing: " << filePath; + return; + } + + QTextStream stream(&file); + + // Find the row index of the selected gene for column 0 + int geneRowIndex = -1; + for (int row = 0; row < model->rowCount(); ++row) { + if (model->data(model->index(row, 0)).toString() == _selectedGene.getString()) { + geneRowIndex = row; + break; + } + } + + + qDebug() << "Gene row index: " << geneRowIndex; + + // If the gene row index is found, write the matching row + + if (model->columnCount()==4) { + //ID,Species Appearance,Gene Appearance Species Names,Statistics + QString ergicString = "Fraction in "+ _topSelectedHierarchyStatus.getString(); + QStringList headers = { "Gene", "Species Appearance", "Species", "Mean Gene Differential Expression", "Gene Appearance Rank", "Fraction in Neuronal", ergicString ,"Count of Selected", "Mean Gene Expression of Selected", "Count of Non Selected","Mean Gene Expression of Non Selected" }; + QString headerNames = headers.join(","); + stream << headerNames; + stream << "\n"; + + if (geneRowIndex != -1) + { + QString geneName = model->data(model->index(geneRowIndex, 0)).toString(); + QString speciesAppearance = model->data(model->index(geneRowIndex, 1)).toString(); + QString geneAppearanceSpeciesNames = model->data(model->index(geneRowIndex, 2)).toString(); + QStringList geneAppearanceSpeciesNamesList = geneAppearanceSpeciesNames.split(";"); + QString statistics = model->data(model->index(geneRowIndex, 3)).toString(); + QStringList statisticsList = statistics.split("\n"); + if (!statisticsList.isEmpty() && statisticsList.last().isEmpty()) { + statisticsList.removeLast(); + } + QString finalString=computeMapFromStatistics(geneName, geneAppearanceSpeciesNamesList, statisticsList); + stream << finalString; + + } + else + { + for (int row = 0; row < model->rowCount(); ++row) { + QString geneName = model->data(model->index(row, 0)).toString(); + QString speciesAppearance = model->data(model->index(row, 1)).toString(); + QString geneAppearanceSpeciesNames = model->data(model->index(row, 2)).toString(); + QStringList geneAppearanceSpeciesNamesList = geneAppearanceSpeciesNames.split(";"); + QString statistics = model->data(model->index(row, 3)).toString(); + QStringList statisticsList = statistics.split("\n"); + if (!statisticsList.isEmpty() && statisticsList.last().isEmpty()) { + statisticsList.removeLast(); + } + QString finalString = computeMapFromStatistics(geneName, geneAppearanceSpeciesNamesList, statisticsList); + stream << finalString; + if (row < model->rowCount() - 1) { + stream << "\n"; + } + } + } + + + } + else + { + // Write header + for (int col = 0; col < model->columnCount(); ++col) { + if (col > 0) { + stream << ","; + } + QString headerVal = model->headerData(col, Qt::Horizontal).toString(); + // Remove all occurrences of "\n" from the headerVal + headerVal.replace("\n", ""); + stream << headerVal; + } + stream << "\n"; + + + + for (int row = 0; row < model->rowCount(); ++row) { + for (int col = 0; col < model->columnCount(); ++col) { + if (col > 0) { + stream << ","; + } + stream << model->data(model->index(row, col)).toString(); + } + stream << "\n"; + } + } + + file.close(); +} + +void SettingsAction::populatePointDataConcurrently(QString datasetId, const std::vector& pointVector, int numPoints, int numDimensions, std::vector dimensionNames) +{ + (void)QtConcurrent::run([this, datasetId, pointVector, numPoints, numDimensions, dimensionNames]() { + auto pointDataset = mv::data().getDataset(datasetId); + + if (pointDataset.isValid()) + { + pointDataset->setSelectionIndices({}); + if (!pointVector.empty() && numPoints > 0 && numDimensions > 0) { + pointDataset->setData(pointVector.data(), numPoints, numDimensions); + pointDataset->setDimensionNames(dimensionNames); + mv::events().notifyDatasetDataChanged(pointDataset); + } + } + }); +} +void SettingsAction::enableActions() +{ + //_startComputationTriggerAction.setDisabled(false); + _topNGenesFilter.setDisabled(false); + _typeofTopNGenes.setDisabled(false); + _clusterCountSortingType.setDisabled(false); + _scatterplotReembedColorOption.setDisabled(false); + _applyLogTransformation.setDisabled(false); + _toggleScatterplotSelection.setDisabled(false); + _usePreComputedTSNE.setDisabled(false); + _tsnePerplexity.setDisabled(false); + _performGeneTableTsnePerplexity.setDisabled(false); + _performGeneTableTsneKnn.setDisabled(false); + _performGeneTableTsneDistance.setDisabled(false); + _performGeneTableTsneTrigger.setDisabled(false); + _computeTreesToDisplayFromHierarchy.setDisabled(false); + _referenceTreeDataset.setDisabled(false); + _mainPointsDataset.setDisabled(false); + _embeddingDataset.setDisabled(false); + _speciesNamesDataset.setDisabled(false); + _bottomClusterNamesDataset.setDisabled(false); + _middleClusterNamesDataset.setDisabled(false); + _topClusterNamesDataset.setDisabled(false); + _speciesExplorerInMap.setDisabled(false); + _topHierarchyClusterNamesFrequencyInclusionList.setDisabled(false); + _speciesExplorerInMapTrigger.setDisabled(false); + _saveGeneTable.setDisabled(false); + _saveSpeciesTable.setDisabled(false); + _revertRowSelectionChangesToInitial.setDisabled(false); + _scatterplotEmbeddingPointsUMAPOption.setDisabled(false); + _selectedSpeciesVals.setDisabled(false); + _clusterOrderHierarchy.setDisabled(false); + _rightClickedCluster.setDisabled(false); + _topSelectedHierarchyStatus.setDisabled(false); + _clearRightClickedCluster.setDisabled(false); + _statusColorAction.setDisabled(false); + _searchBox->setDisabled(false); + enableDisableButtonsAutomatically(); + if (_statusColorAction.getString() == "C") + { + _startComputationTriggerAction.setDisabled(true); + } + else + { + _startComputationTriggerAction.setDisabled(false); + } + _toggleScatterplotSelection.setChecked(true); + QApplication::processEvents(); +} +void SettingsAction::disableActions() +{ + _statusColorAction.setString("R"); + _clearRightClickedCluster.trigger(); + _startComputationTriggerAction.setDisabled(true); + _topNGenesFilter.setDisabled(true); + _typeofTopNGenes.setDisabled(true); + _clusterCountSortingType.setDisabled(true); + _scatterplotReembedColorOption.setDisabled(true); + _removeRowSelection.setDisabled(true); + _speciesExplorerInMapTrigger.setDisabled(true); + _saveGeneTable.setDisabled(true); + _saveSpeciesTable.setDisabled(true); + _usePreComputedTSNE.setDisabled(true); + _applyLogTransformation.setDisabled(true); + _speciesExplorerInMap.setDisabled(true); + _revertRowSelectionChangesToInitial.setDisabled(true); + _toggleScatterplotSelection.setDisabled(true); + _tsnePerplexity.setDisabled(true); + _performGeneTableTsnePerplexity.setDisabled(true); + _performGeneTableTsneKnn.setDisabled(true); + _performGeneTableTsneDistance.setDisabled(true); + _performGeneTableTsneTrigger.setDisabled(true); + _computeTreesToDisplayFromHierarchy.setDisabled(true); + _referenceTreeDataset.setDisabled(true); + _mainPointsDataset.setDisabled(true); + _embeddingDataset.setDisabled(true); + _speciesNamesDataset.setDisabled(true); + _bottomClusterNamesDataset.setDisabled(true); + _middleClusterNamesDataset.setDisabled(true); + _topClusterNamesDataset.setDisabled(true); + _scatterplotEmbeddingPointsUMAPOption.setDisabled(true); + _topHierarchyClusterNamesFrequencyInclusionList.setDisabled(true); + _selectedSpeciesVals.setDisabled(true); + _clusterOrderHierarchy.setDisabled(true); + _rightClickedCluster.setDisabled(true); + _topSelectedHierarchyStatus.setDisabled(true); + _clearRightClickedCluster.setDisabled(true); + _statusColorAction.setDisabled(true); + _searchBox->setDisabled(true); + QApplication::processEvents(); +} + +void SettingsAction::enableDisableButtonsAutomatically() +{ + + bool optionsActionHasOptions = !_speciesExplorerInMap.getOptions().isEmpty(); + bool stringActionHasOptions = !_selectedSpeciesVals.getString().isEmpty(); + + bool bothListsEqual = false; + if (optionsActionHasOptions && stringActionHasOptions) { + QStringList temp = _selectedSpeciesVals.getString().split(" @%$,$%@ "); + QStringList species = _speciesExplorerInMap.getSelectedOptions(); + + std::sort(temp.begin(), temp.end()); + std::sort(species.begin(), species.end()); + bothListsEqual = (temp == species); + } + _revertRowSelectionChangesToInitial.setDisabled(false); + _speciesExplorerInMapTrigger.setDisabled(false); + //if (!stringActionHasOptions) + //{ + // _revertRowSelectionChangesToInitial.setDisabled(true); + //} + //else + //{ + // if (!optionsActionHasOptions) + // { + + // _revertRowSelectionChangesToInitial.setDisabled(false); + // } + // else + // { + // if (bothListsEqual) + // { + + // _revertRowSelectionChangesToInitial.setDisabled(true); + // } + // else + // { + + // _revertRowSelectionChangesToInitial.setDisabled(false); + // } + // } + //} + + + + //if (!optionsActionHasOptions) + //{ + // _speciesExplorerInMapTrigger.setDisabled(true); + + //} + //else + //{ + // _speciesExplorerInMapTrigger.setDisabled(false); + //} + + + +} + + +void SettingsAction::populatePointData(QString& datasetId, std::vector& pointVector, int& numPoints, int& numDimensions, std::vector& dimensionNames) +{ + auto pointDataset = mv::data().getDataset(datasetId); + + if (pointDataset.isValid()) + { + pointDataset->setSelectionIndices({}); + if (pointVector.size() > 0 && numPoints > 0 && numDimensions > 0) { + pointDataset->setData(pointVector.data(), numPoints, numDimensions); + pointDataset->setDimensionNames(dimensionNames); + mv::events().notifyDatasetDataChanged(pointDataset); + } + + } +} +void SettingsAction::populateClusterData(QString& datasetId, std::map>>& clusterMap) +{ + + auto colorDataset = mv::data().getDataset(datasetId); + if (colorDataset.isValid()) + { + colorDataset->getClusters() = QVector(); + for (const auto& pair : clusterMap) + { + QString clusterName = pair.first; + std::pair> value = pair.second; + QColor clusterColor = value.first; + std::vector clusterIndices(value.second.begin(), value.second.end()); + + if (clusterIndices.size() > 0) + { + Cluster clusterValue; + clusterValue.setName(clusterName); + clusterValue.setColor(clusterColor); + clusterValue.setIndices(clusterIndices); + colorDataset->addCluster(clusterValue); + } + } + + mv::events().notifyDatasetDataChanged(colorDataset); + } + + +} + +void SettingsAction::clearTableSelection(QTableView* tableView) { + if (tableView && tableView->selectionModel()) { + // Clear the current selection + tableView->clearSelection(); + + // Temporarily disable the selection mode to remove highlight + QAbstractItemView::SelectionMode oldMode = tableView->selectionMode(); + tableView->setSelectionMode(QAbstractItemView::NoSelection); + + // Clear the current index + tableView->selectionModel()->setCurrentIndex(QModelIndex(), QItemSelectionModel::NoUpdate); + + // Restore the original selection mode + tableView->setSelectionMode(oldMode); + + // Update the view to ensure changes are reflected + tableView->update(); + } + else { + qDebug() << "TableView or its selection model is null"; + } +} + +void SettingsAction::removeSelectionTableRows(QStringList* selectedLeaves) +{ + //check if _selectionDetailsTable is valid + if (_selectionDetailsTable == nullptr) { + return; + } + + clearTableSelection(_selectionDetailsTable); + + QAbstractItemModel* model = _selectionDetailsTable->model(); + + //check if model is valid + if (model == nullptr) { + return; + } + + //auto colorValues = getSystemModeColor(); + //auto systemColor = colorValues[0]; + //auto valuesColor = colorValues[1]; + + // Iterate through all rows + for (int row = 0; row < model->rowCount(); ++row) { + QModelIndex index = model->index(row, 0); // Assuming species name is in column 0 + QString species = model->data(index, Qt::UserRole).toString(); + + // Check if the species is one of the selected species + if (selectedLeaves->contains(species)) { + for (int col = 0; col < model->columnCount(); ++col) { + QModelIndex cellIndex = model->index(row, col); + _selectionDetailsTable->model()->setData(cellIndex, QBrush(QColor("#00A2ED")), Qt::BackgroundRole); + _selectionDetailsTable->model()->setData(cellIndex, QBrush(QColor("#000000")), Qt::ForegroundRole); + } + } + else + { + //remove existing color from rows + for (int col = 0; col < model->columnCount(); ++col) { + QModelIndex cellIndex = model->index(row, col); + _selectionDetailsTable->model()->setData(cellIndex, QBrush(QColor("#FFFFFF")), Qt::BackgroundRole); + _selectionDetailsTable->model()->setData(cellIndex, QBrush(QColor("#000000")), Qt::ForegroundRole); + } + } + } + +} + diff --git a/src/SettingsAction.Serialization.inl b/src/SettingsAction.Serialization.inl new file mode 100644 index 0000000..0c64db3 --- /dev/null +++ b/src/SettingsAction.Serialization.inl @@ -0,0 +1,115 @@ +inline SettingsAction::OptionSelectionAction::OptionSelectionAction(SettingsAction& SettingsAction) : + GroupAction(nullptr, "CrossSpeciesComparisonGeneDetectPluginOptionSelectionAction"), + _settingsAction(SettingsAction) +{ + setText("Options"); + setIcon(mv::util::StyledIcon("wrench")); + //addAction(&_settingsAction.getTableModelAction()); + //addAction(&_settingsAction.getSelectedGeneAction()); + //addAction(&_settingsAction.getSelectedRowIndexAction()); + //addAction(&_settingsAction.getFilteringTreeDatasetAction()); + //addAction(&_settingsAction.getOptionSelectionAction()); + //addAction(&_settingsAction.getStartComputationTriggerAction()); + //addAction(&_settingsAction.getReferenceTreeDatasetAction()); + //addAction(&_settingsAction.getMainPointsDataset()); + //addAction(&_settingsAction.getHierarchyTopClusterDataset()); + //addAction(&_settingsAction.getHierarchyMiddleClusterDataset()); + //addAction(&_settingsAction.getHierarchyBottomClusterDataset()); + //addAction(&_settingsAction.getSpeciesNamesDataset()); + //addAction(&_settingsAction.getSelectedClusterNames()); + + +} + + +void SettingsAction::fromVariantMap(const QVariantMap& variantMap) +{ + WidgetAction::fromVariantMap(variantMap); + + _geneNamesConnection.fromParentVariantMap(variantMap); + _createRowMultiSelectTree.fromParentVariantMap(variantMap); + _listModel.fromParentVariantMap(variantMap); + _selectedGene.fromParentVariantMap(variantMap); + _mainPointsDataset.fromParentVariantMap(variantMap); + _embeddingDataset.fromParentVariantMap(variantMap); + _speciesNamesDataset.fromParentVariantMap(variantMap); + _bottomClusterNamesDataset.fromParentVariantMap(variantMap); + _middleClusterNamesDataset.fromParentVariantMap(variantMap); + _topClusterNamesDataset.fromParentVariantMap(variantMap); + _filteredGeneNamesVariant.fromParentVariantMap(variantMap); + _topNGenesFilter.fromParentVariantMap(variantMap); + _filteringEditTreeDataset.fromParentVariantMap(variantMap); + _referenceTreeDataset.fromParentVariantMap(variantMap); + _selectedRowIndex.fromParentVariantMap(variantMap); + _performGeneTableTsneAction.fromParentVariantMap(variantMap); + _tsnePerplexity.fromParentVariantMap(variantMap); + _performGeneTableTsnePerplexity.fromParentVariantMap(variantMap); + _performGeneTableTsneKnn.fromParentVariantMap(variantMap); + _performGeneTableTsneDistance.fromParentVariantMap(variantMap); + _performGeneTableTsneTrigger.fromParentVariantMap(variantMap); + _hiddenShowncolumns.fromParentVariantMap(variantMap); + _speciesExplorerInMap.fromParentVariantMap(variantMap); + _topHierarchyClusterNamesFrequencyInclusionList.fromParentVariantMap(variantMap); + _scatterplotReembedColorOption.fromParentVariantMap(variantMap); + _scatterplotEmbeddingPointsUMAPOption.fromParentVariantMap(variantMap); + _selectedSpeciesVals.fromParentVariantMap(variantMap); + _clusterOrderHierarchy.fromParentVariantMap(variantMap); + _rightClickedCluster.fromParentVariantMap(variantMap); + _topSelectedHierarchyStatus.fromParentVariantMap(variantMap); + _clearRightClickedCluster.fromParentVariantMap(variantMap); + _removeRowSelection.fromParentVariantMap(variantMap); + _revertRowSelectionChangesToInitial.fromParentVariantMap(variantMap); + _speciesExplorerInMapTrigger.fromParentVariantMap(variantMap); + _statusColorAction.fromParentVariantMap(variantMap); + _typeofTopNGenes.fromParentVariantMap(variantMap); + _clusterCountSortingType.fromParentVariantMap(variantMap); + _usePreComputedTSNE.fromParentVariantMap(variantMap); + _applyLogTransformation.fromParentVariantMap(variantMap); + +} + +QVariantMap SettingsAction::toVariantMap() const +{ + QVariantMap variantMap = WidgetAction::toVariantMap(); + + _geneNamesConnection.insertIntoVariantMap(variantMap); + _createRowMultiSelectTree.insertIntoVariantMap(variantMap); + _listModel.insertIntoVariantMap(variantMap); + _selectedGene.insertIntoVariantMap(variantMap); + _mainPointsDataset.insertIntoVariantMap(variantMap); + _embeddingDataset.insertIntoVariantMap(variantMap); + _speciesNamesDataset.insertIntoVariantMap(variantMap); + _bottomClusterNamesDataset.insertIntoVariantMap(variantMap); + _middleClusterNamesDataset.insertIntoVariantMap(variantMap); + _topClusterNamesDataset.insertIntoVariantMap(variantMap); + _filteredGeneNamesVariant.insertIntoVariantMap(variantMap); + _topNGenesFilter.insertIntoVariantMap(variantMap); + _filteringEditTreeDataset.insertIntoVariantMap(variantMap); + _referenceTreeDataset.insertIntoVariantMap(variantMap); + _selectedRowIndex.insertIntoVariantMap(variantMap); + _performGeneTableTsneAction.insertIntoVariantMap(variantMap); + _tsnePerplexity.insertIntoVariantMap(variantMap); + _performGeneTableTsnePerplexity.insertIntoVariantMap(variantMap); + _performGeneTableTsneDistance.insertIntoVariantMap(variantMap); + _performGeneTableTsneKnn.insertIntoVariantMap(variantMap); + _performGeneTableTsneTrigger.insertIntoVariantMap(variantMap); + _hiddenShowncolumns.insertIntoVariantMap(variantMap); + _speciesExplorerInMap.insertIntoVariantMap(variantMap); + _topHierarchyClusterNamesFrequencyInclusionList.insertIntoVariantMap(variantMap); + _scatterplotReembedColorOption.insertIntoVariantMap(variantMap); + _scatterplotEmbeddingPointsUMAPOption.insertIntoVariantMap(variantMap); + _selectedSpeciesVals.insertIntoVariantMap(variantMap); + _clusterOrderHierarchy.insertIntoVariantMap(variantMap); + _rightClickedCluster.insertIntoVariantMap(variantMap); + _topSelectedHierarchyStatus.insertIntoVariantMap(variantMap); + _clearRightClickedCluster.insertIntoVariantMap(variantMap); + _removeRowSelection.insertIntoVariantMap(variantMap); + _revertRowSelectionChangesToInitial.insertIntoVariantMap(variantMap); + _speciesExplorerInMapTrigger.insertIntoVariantMap(variantMap); + _statusColorAction.insertIntoVariantMap(variantMap); + _typeofTopNGenes.insertIntoVariantMap(variantMap); + _clusterCountSortingType.insertIntoVariantMap(variantMap); + _usePreComputedTSNE.insertIntoVariantMap(variantMap); + _applyLogTransformation.insertIntoVariantMap(variantMap); + return variantMap; +} diff --git a/src/SettingsAction.Tree.inl b/src/SettingsAction.Tree.inl new file mode 100644 index 0000000..a586e9e --- /dev/null +++ b/src/SettingsAction.Tree.inl @@ -0,0 +1,287 @@ +QString SettingsAction::generateTooltip(const ViewPluginSamplerAction::SampleContext& toolTipContext, const QString& clusterDatasetId, bool showTooltip, QString indicesType) { + // Extract and convert GlobalPointIndices and ColorDatasetID from toolTipContext + auto raw_Global_Local_PointIndices = toolTipContext[indicesType].toList(); + + // Convert the list of global point indices to a vector of integers + std::vector global_local_PointIndices; + global_local_PointIndices.reserve(raw_Global_Local_PointIndices.size()); + for (const auto& global_local_PointIndex : raw_Global_Local_PointIndices) { + global_local_PointIndices.push_back(global_local_PointIndex.toInt()); + } + + // If the global point indices list is empty, return an empty result + if (global_local_PointIndices.empty()) { + return {}; + } + + // If there is no cluster dataset ID, return a summary of total points + if (clusterDatasetId.isEmpty()) { + return QString("
Total points: %1
").arg(global_local_PointIndices.size()); + } + + // Retrieve the cluster dataset + auto clusterFullDataset = mv::data().getDataset(clusterDatasetId); + + // If the dataset is invalid, return a summary of total points + if (!clusterFullDataset.isValid()) { + return QString("
Total points: %1
").arg(global_local_PointIndices.size()); + } + + // Get the clusters from the dataset + auto clusterValuesData = clusterFullDataset->getClusters(); + + // If the clusters data is empty, return a summary of total points + if (clusterValuesData.isEmpty()) { + return QString("
Total points: %1
").arg(global_local_PointIndices.size()); + } + + // Process each cluster and find intersections with global point indices + std::map> clusterCountMap; + for (const auto& cluster : clusterValuesData) { + QString clusterName = cluster.getName(); + QColor clusterColor = cluster.getColor(); + auto clusterIndices = cluster.getIndices(); + + // Sort the indices before performing the intersection + std::sort(clusterIndices.begin(), clusterIndices.end()); + std::sort(global_local_PointIndices.begin(), global_local_PointIndices.end()); + + std::vector intersect; + std::set_intersection(clusterIndices.begin(), clusterIndices.end(), + global_local_PointIndices.begin(), global_local_PointIndices.end(), + std::back_inserter(intersect)); + + // If there is an intersection, store the result in the map + if (!intersect.empty()) { + clusterCountMap[clusterName] = std::make_pair(intersect.size(), clusterColor); + } + } + + // If no clusters were found, return a summary of total points + if (clusterCountMap.empty()) { + return QString("
Total points: %1
").arg(global_local_PointIndices.size()); + } + + // Generate HTML output + QString html = ""; + + // Convert the map to a vector of pairs for sorting + std::vector>> clusterVector(clusterCountMap.begin(), clusterCountMap.end()); + + // Sort the vector by count in descending order + std::sort(clusterVector.begin(), clusterVector.end(), [](const auto& a, const auto& b) { + return a.second.first > b.second.first; + }); + + // Find the maximum count for scaling the bars + int maxCount = clusterVector.empty() ? 1 : clusterVector.front().second.first; // Default to 1 if no data. + + // Calculate the maximum width required for text and icon + int maxTextIconWidth = 0; + for (const auto& entry : clusterVector) { + QString clusterName = entry.first; + int textWidth = QFontMetrics(QFont()).horizontalAdvance(clusterName + ": " + QString::number(entry.second.first)); + int iconWidth = 16; // Assuming icon width is 16px + maxTextIconWidth = std::max(maxTextIconWidth, textWidth + iconWidth); + } + maxTextIconWidth = maxTextIconWidth + 2; + // Populate the divs with cluster data + html += "
"; + for (const auto& entry : clusterVector) { + QString clusterName = entry.first; + int count = entry.second.first; + QString colorHex = "#a6a6a6"; // entry.second.second.name(); + QColor color(entry.second.second); + + QString textColor = "black"; + int barWidth = (maxCount > 0) ? static_cast((static_cast(count) / maxCount) * 100) : 0; + barWidth = std::max(barWidth, 5); // Minimum width for visibility + + QString iconPath = ":/speciesicons/SpeciesIcons/" + clusterName + ".svg"; + QString iconHtml; + if (QFile::exists(iconPath)) { + QFile file(iconPath); + if (file.open(QIODevice::ReadOnly)) { + QByteArray iconData = file.readAll().toBase64(); + iconHtml = QString(" ").arg(QString(iconData)); + } else { + iconHtml = "
"; // Placeholder + } + } else { + iconHtml = "
"; // Placeholder + } + + html += "
"; + html += "
"; + html += "
" + + iconHtml + clusterName + ": " + QString::number(count) + "
"; + html += "
"; + } + + html += "
"; + + html += ""; + + + return html; +} + + +void SettingsAction::updateSelectedSpeciesCounts(QJsonObject& node, const std::map& speciesCountMap) { + // Check if the "name" key exists in the current node + if (node.contains("name")) { + QString nodeName = node["name"].toString(); + auto it = speciesCountMap.find(nodeName); + // If the "name" is found in the speciesExpressionMap, update "mean" if it exists or add "mean" if it doesn't exist + if (it != speciesCountMap.end()) { + node["cellCounts"] = it->second; // Use it->second to access the value in the map + } + } + + // If the node has "children", recursively update them as well + if (node.contains("children")) { + QJsonArray children = node["children"].toArray(); + for (int i = 0; i < children.size(); ++i) { + QJsonObject child = children[i].toObject(); + updateSelectedSpeciesCounts(child, speciesCountMap); // Recursive call + children[i] = child; // Update the modified object back into the array + } + node["children"] = children; // Update the modified array back into the parent JSON object + } +} +/* +QString SettingsAction::createJsonTreeFromNewick(QString tree, std::vector leafnames, std::map speciesMeanValues) +{ + int i = 0; + std::string jsonString = ""; + std::stringstream jsonStream; + std::string newick = tree.toStdString(); + while (i < newick.size()) { + if (newick[i] == '(') { + jsonStream << "{\n\"children\": ["; + i++; + } + else if (newick[i] == ',') { + jsonStream << ","; + i++; + } + else if (newick[i] == ')') { + jsonStream << "],\n\"id\": 1,\n\"score\": 1,\n\"branchLength\": 1.0,\n\"width\": 1\n}"; + i++; + } + else if (newick[i] == ';') { + break; + } + else { + if (isdigit(newick[i])) { + int skip = 1; + std::string num = ""; + for (int j = i; j < newick.size(); j++) { + if (isdigit(newick[j])) { + continue; + } + else { + num = newick.substr(i, j - i); + + skip = j - i; + break; + } + } + std::string species = leafnames[(std::stoi(num) - 1)].toStdString(); + //std::string meanValue = std::to_string(speciesMeanValues[QString::fromStdString(species)]); + auto it = speciesMeanValues.find(QString::fromStdString(species)); + std::string meanValue; + if (it != speciesMeanValues.end()) { + // Key found, use the corresponding value + meanValue = std::to_string(it->second.meanSelected); + } + else { + // Key not found, assign -1 + meanValue = "-1"; + } + + jsonStream << "{\n\"color\": \"#000000\",\n\"hastrait\": true,\n\"iscollapsed\": false,\n\"branchLength\": 1.0,\n\"cellCounts\": " << 0 << ",\n\"mean\": "<< meanValue <<", \n\"name\": \"" << species << "\"\n}"; + i += skip; + } + } + } + + jsonString = jsonStream.str(); + + nlohmann::json json = nlohmann::json::parse(jsonString); + std::string jsonStr = json.dump(4); + //qDebug()<< "CrossSpeciesComparisonClusterRankPlugin::createJsonTree: jsonStr: " << QString::fromStdString(jsonStr); + QString formattedTree = QString::fromStdString(jsonStr); + + + return formattedTree; +} +*/ +/* +std::string SettingsAction::mergeToNewick(int* merge, int numOfLeaves) { + std::vector labels(numOfLeaves); + for (int i = 0; i < numOfLeaves; ++i) { + labels[i] = std::to_string(i + 1); + } + + std::stack stack; + + for (int i = 0; i < 2 * (numOfLeaves - 1); i += 2) { + int left = merge[i]; + int right = merge[i + 1]; + + std::string leftStr; + if (left < 0) { + leftStr = labels[-left - 1]; + } + else { + leftStr = stack.top(); + stack.pop(); + } + + std::string rightStr; + if (right < 0) { + rightStr = labels[-right - 1]; + } + else { + rightStr = stack.top(); + stack.pop(); + } + + std::string merged = "(" + leftStr + "," + rightStr + ")"; + stack.push(merged); + } + + return stack.top() + ";"; +} +*/ +double* SettingsAction::condensedDistanceMatrix(const std::vector& items) { + size_t n = items.size(); + double* distmat = new double[(n * (n - 1)) / 2]; + size_t k = 0; + +#pragma omp parallel for schedule(dynamic) collapse(2) private(k) + for (size_t i = 0; i < n; ++i) { + for (size_t j = i + 1; j < n; ++j) { + k = ((n * (n - 1)) / 2) - ((n - i) * (n - i - 1)) / 2 + j - i - 1; + distmat[k] = std::abs(items[i] - items[j]); + } + } + + return distmat; +} + +SettingsAction::Widget::Widget(QWidget* parent, SettingsAction* SettingsAction) : + WidgetActionWidget(parent, SettingsAction) +{ } + +SettingsAction::OptionSelectionAction::Widget::Widget(QWidget* parent, OptionSelectionAction* optionSelectionAction) : + WidgetActionWidget(parent, optionSelectionAction) +{ } + diff --git a/src/SettingsAction.Ui.inl b/src/SettingsAction.Ui.inl new file mode 100644 index 0000000..afba99d --- /dev/null +++ b/src/SettingsAction.Ui.inl @@ -0,0 +1,1219 @@ +SettingsAction::SettingsAction(CrossSpeciesComparisonGeneDetectPlugin& CrossSpeciesComparisonGeneDetectPlugin) : + WidgetAction(&CrossSpeciesComparisonGeneDetectPlugin, "CrossSpeciesComparisonGeneDetectPlugin Settings"), + _crossSpeciesComparisonGeneDetectPlugin(CrossSpeciesComparisonGeneDetectPlugin), + _listModel(this, "List Model"), + _selectedGene(this, "Selected Gene"), + _filteringEditTreeDataset(this, "Filtering Tree Dataset"), + _selectedRowIndex(this, "Selected Row Index"), + _optionSelectionAction(*this), + _startComputationTriggerAction(this, "Update"), + _referenceTreeDataset(this, "Reference Tree Dataset"), + _mainPointsDataset(this, "Main Points Dataset"), + _embeddingDataset(this, "Embedding Dataset"), + //_hierarchyBottomClusterDataset(this, "Hierarchy Bottom Cluster Dataset"), + //_hierarchyMiddleClusterDataset(this, "Hierarchy Middle Cluster Dataset"), + //_hierarchyTopClusterDataset(this, "Hierarchy Top Cluster Dataset"), + _speciesNamesDataset(this, "Species Names"), + _bottomClusterNamesDataset(this, "Bottom Cluster Names"), + _middleClusterNamesDataset(this, "Middle Cluster Names"), + _topClusterNamesDataset(this, "Top Cluster Names"), + //_calculationReferenceCluster(this, "Calculation Reference Cluster"), + _filteredGeneNamesVariant(this, "Filtered Gene Names"), + _topNGenesFilter(this, "Top N"), + _geneNamesConnection(this, "Gene Names Connection"), + _createRowMultiSelectTree(this, "Create Row MultiSelect Tree"), + _performGeneTableTsneAction(this, "Perform Gene Table TSNE"), + _tsnePerplexity(this, "TSNE Perplexity"), + _hiddenShowncolumns(this, "Hidden Shown Columns"), + _scatterplotReembedColorOption(this, "Embed Color"), + _scatterplotEmbeddingPointsUMAPOption(this, "Embedding UMAP Points"), + _selectedSpeciesVals(this, "Selected Species Vals"), + _removeRowSelection(this, "DeSelect"), + _revertRowSelectionChangesToInitial(this, "Revert"), + _statusColorAction(this, "Status color"), + _typeofTopNGenes(this, "N Type"), + _usePreComputedTSNE(this, "Use Precomputed TSNE"), + _speciesExplorerInMap(this, "Leaves Explorer Options"), + _topHierarchyClusterNamesFrequencyInclusionList(this, "Top Hierarchy Cluster Names Frequency Inclusion List"), + _speciesExplorerInMapTrigger(this, "Explore"), + _applyLogTransformation(this, "Gene mapping log"), + _clusterCountSortingType(this, "Cluster Count Sorting Type"), + _currentCellSelectionClusterInfoLabel(nullptr), + _performGeneTableTsnePerplexity(this, "Perform Gene Table TSNE Perplexity"), + _performGeneTableTsneKnn(this, "Perform Gene Table TSNE Knn"), + _performGeneTableTsneDistance(this, "Perform Gene Table TSNE Distance"), + _performGeneTableTsneTrigger(this, "Perform Gene Table TSNE Trigger"), + _computeTreesToDisplayFromHierarchy(this, "Compute Trees To Display From Hierarchy"), + _clusterOrderHierarchy(this, "Cluster Order Hierarchy"), + _rightClickedCluster(this, "Right Clicked Cluster"), + _topSelectedHierarchyStatus(this, "Top Selected Hierarchy Status"), + _clearRightClickedCluster(this, "Clear Right Clicked Cluster"), + _toggleScatterplotSelection(this, "Show Scatterplot Selection"), + _mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker(this, "Map For Hierarchy Items Change Method Stop For Project Load Blocker"), + _saveSpeciesTable(this, "Save Left Gene Table"), + _saveGeneTable(this, "Save Right Species Selection Table") +{ + _mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.setChecked(true); + setSerializationName("CSCGDV:CrossSpeciesComparison Gene Detect Plugin Settings"); + _statusBarActionWidget = new QStatusBar(); + + + _popupMessageInit = new QMessageBox(); + _popupMessageInit->setIcon(QMessageBox::Information); + _popupMessageInit->setWindowTitle("Computation in Progress"); + _popupMessageInit->setText("Data Precomputation in Progress"); + _popupMessageInit->setInformativeText( + "The system is currently precomputing essential data to enhance your interactive exploration experience. " + "This process may take some time based on the input data size, your memory and processor.
This popup will close automatically once initialization is complete. " + "Thank you for using Cytosplore EvoViewer.

" + "Please visit our website https://viewer.cytosplore.org/ to learn more." + ); + _popupMessageInit->setTextFormat(Qt::RichText); // Enable rich text formatting + _popupMessageInit->setTextInteractionFlags(Qt::TextBrowserInteraction); // Enable link interaction + _popupMessageInit->setStandardButtons(QMessageBox::NoButton); + _popupMessageInit->setModal(true); + + // Create a message box to notify the user about the completion of the tree creation process + _popupMessageTreeCreationCompletion = new QMessageBox(); + _popupMessageTreeCreationCompletion->setIcon(QMessageBox::Information); + _popupMessageTreeCreationCompletion->setWindowTitle("Tree Creation Completed"); + _popupMessageTreeCreationCompletion->setText( + "The precomputed tree method has been completed. " + "Right-click is available in the hierarchy view. " + "To explore expression values across species for each cluster in the phylogenetic tree, " + ); + _popupMessageTreeCreationCompletion->setInformativeText( + "Right Click available in the cluster hierarchy view." + ); + _popupMessageTreeCreationCompletion->setStandardButtons(QMessageBox::Ok); + _popupMessageTreeCreationCompletion->setModal(false); + /* + _popupMessageInit = new QMessageBox(); + _popupMessageInit->setIcon(QMessageBox::Information); + _popupMessageInit->setWindowTitle("Computation in Progress"); + _popupMessageInit->setText( + "
" + "

Data Precomputation in Progress

" + "
" + ); + _popupMessageInit->setInformativeText( + "
" + "The system is currently precomputing essential data to enhance your interactive exploration experience. " + "This process may take some time based on your data size and processor. The popup will close automatically " + "once initialization is complete. Thank you for using Cytosplore EvoViewer." + "
" + ); + _popupMessageInit->setStandardButtons(QMessageBox::NoButton); + _popupMessageInit->setModal(true); + */ + + _searchBox = new CustomLineEdit(); + QIcon searchIcon = mv::util::StyledIcon("search"); + QAction* searchAction = new QAction(_searchBox); + searchAction->setIcon(searchIcon); + _searchBox->addAction(searchAction, QLineEdit::LeadingPosition); + _searchBox->setPlaceholderText("Search ID..."); + _searchBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + _searchBox->setMaximumHeight(22); + + //_searchBox->setMinimumWidth(100); + _searchBox->setMaximumWidth(800); + _searchBox->setAutoFillBackground(true); + _searchBox->setStyleSheet("QLineEdit { background-color: white; }"); + _searchBox->setClearButtonEnabled(false); + _searchBox->setFocusPolicy(Qt::StrongFocus); + _meanMapComputed = false; + _statusBarActionWidget->setStatusTip("Status"); + _statusBarActionWidget->setMaximumHeight(22); + //_statusBarActionWidget->setFixedWidth(120); + //_statusBarActionWidget->setMinimumWidth(100); + _statusBarActionWidget->setMaximumWidth(800); + _statusBarActionWidget->setAutoFillBackground(true); + _statusBarActionWidget->setSizeGripEnabled(false); + + _geneTableView = new QTableView(); + _selectionDetailsTable = new QTableView(); + + QPalette palette = _geneTableView->palette(); + palette.setColor(QPalette::Base, Qt::white); // Background color + palette.setColor(QPalette::Text, Qt::black); // Text color + + _geneTableView->setPalette(palette); + _selectionDetailsTable->setPalette(palette); + + + _splitter = new QHBoxLayout(); + _geneTableView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + _geneTableView->setSelectionBehavior(QAbstractItemView::SelectRows); + _geneTableView->setSelectionMode(QAbstractItemView::SingleSelection); + _geneTableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + _geneTableView->setAlternatingRowColors(false); + _geneTableView->setSortingEnabled(true); + _geneTableView->setShowGrid(true); + _geneTableView->setGridStyle(Qt::SolidLine); + _geneTableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + _geneTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + _geneTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + _geneTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + _geneTableView->setCornerButtonEnabled(false); + _geneTableView->setWordWrap(false); + _geneTableView->setTabKeyNavigation(false); + _geneTableView->setAcceptDrops(false); + _geneTableView->setDropIndicatorShown(false); + _geneTableView->setDragEnabled(false); + _geneTableView->setDragDropMode(QAbstractItemView::NoDragDrop); + _geneTableView->setDragDropOverwriteMode(false); + _geneTableView->setAutoScroll(false); + _geneTableView->setAutoScrollMargin(16); + _geneTableView->setAutoFillBackground(true); + _geneTableView->setFrameShape(QFrame::NoFrame); + _geneTableView->setFrameShadow(QFrame::Plain); + _geneTableView->setLineWidth(0); + _geneTableView->setMidLineWidth(0); + _geneTableView->setFocusPolicy(Qt::NoFocus); + _geneTableView->setContextMenuPolicy(Qt::NoContextMenu); + _geneTableView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + _geneTableView->setMinimumSize(QSize(0, 0)); + _geneTableView->setMaximumSize(QSize(16777215, 16777215)); + _geneTableView->setBaseSize(QSize(0, 0)); + _geneTableView->setFocusPolicy(Qt::StrongFocus); + _geneTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + + //only highlight multiple rows if shiuft is pressed + _geneTableView->setSelectionBehavior(QAbstractItemView::SelectRows); + + // removeDatasets(groupIDDeletion); + //removeDatasets(groupId1); + //removeDatasets(groupId2); + //removeDatasets(groupId3); + + _selectionDetailsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + _selectionDetailsTable->setSelectionBehavior(QAbstractItemView::SelectRows); + _selectionDetailsTable->setSelectionMode(QAbstractItemView::SingleSelection); + _selectionDetailsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + _selectionDetailsTable->setAlternatingRowColors(false); + _selectionDetailsTable->setSortingEnabled(true); + _selectionDetailsTable->setShowGrid(true); + _selectionDetailsTable->setGridStyle(Qt::SolidLine); + _selectionDetailsTable->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + _selectionDetailsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + _selectionDetailsTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + _selectionDetailsTable->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + _selectionDetailsTable->setCornerButtonEnabled(false); + _selectionDetailsTable->setWordWrap(false); + _selectionDetailsTable->setTabKeyNavigation(false); + _selectionDetailsTable->setAcceptDrops(false); + _selectionDetailsTable->setDropIndicatorShown(false); + _selectionDetailsTable->setDragEnabled(false); + _selectionDetailsTable->setDragDropMode(QAbstractItemView::NoDragDrop); + _selectionDetailsTable->setDragDropOverwriteMode(false); + _selectionDetailsTable->setAutoScroll(false); + _selectionDetailsTable->setAutoScrollMargin(16); + _selectionDetailsTable->setAutoFillBackground(true); + _selectionDetailsTable->setFrameShape(QFrame::NoFrame); + _selectionDetailsTable->setFrameShadow(QFrame::Plain); + _selectionDetailsTable->setLineWidth(0); + _selectionDetailsTable->setMidLineWidth(0); + _selectionDetailsTable->setFocusPolicy(Qt::NoFocus); + _selectionDetailsTable->setContextMenuPolicy(Qt::NoContextMenu); + _selectionDetailsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + _selectionDetailsTable->setMinimumSize(QSize(0, 0)); + _selectionDetailsTable->setMaximumSize(QSize(16777215, 16777215)); + _selectionDetailsTable->setBaseSize(QSize(0, 0)); + _selectionDetailsTable->setFocusPolicy(Qt::StrongFocus); + _selectionDetailsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + _selectionDetailsTable->setSelectionBehavior(QAbstractItemView::SelectRows); + + + + _selectedCellClusterInfoStatusBar = new mv::gui::FlowLayout(); + + + _listModel.setSerializationName("CSCGDV:List Model"); + _selectedGene.setSerializationName("CSCGDV:Selected Gene"); + _mainPointsDataset.setSerializationName("CSCGDV:Main Points Dataset"); + _embeddingDataset.setSerializationName("CSCGDV:Embedding Dataset"); + _speciesNamesDataset.setSerializationName("CSCGDV:Species Names Dataset"); + _bottomClusterNamesDataset.setSerializationName("CSCGDV:Cluster Names Dataset"); + _middleClusterNamesDataset.setSerializationName("CSCGDV:Middle Cluster Names Dataset"); + _topClusterNamesDataset.setSerializationName("CSCGDV:Top Cluster Names Dataset"); + _filteredGeneNamesVariant.setSerializationName("CSCGDV:Filtered Gene Names"); + _topNGenesFilter.setSerializationName("CSCGDV:Top N Genes Filter"); + _filteringEditTreeDataset.setSerializationName("CSCGDV:Filtering Tree Dataset"); + _referenceTreeDataset.setSerializationName("CSCGDV:Reference Tree Dataset"); + _selectedRowIndex.setSerializationName("CSCGDV:Selected Row Index"); + _geneNamesConnection.setSerializationName("CSCGDV:Gene Names Connection"); + _selectedSpeciesVals.setSerializationName("CSCGDV:Selected Species Vals"); + _clusterOrderHierarchy.setSerializationName("CSCGDV:Cluster Order Hierarchy"); + _rightClickedCluster.setSerializationName("CSCGDV:Right Clicked Cluster"); + _topSelectedHierarchyStatus.setSerializationName("CSCGDV:Top Selected Hierarchy Status"); + _clearRightClickedCluster.setSerializationName("CSCGDV:Clear Right Clicked Cluster"); + _removeRowSelection.setSerializationName("CSCGDV:Remove Row Selection"); + _removeRowSelection.setDisabled(true); + _revertRowSelectionChangesToInitial.setSerializationName("CSCGDV:Revert Row Selection Changes To Initial"); + _revertRowSelectionChangesToInitial.setDisabled(true); + _speciesExplorerInMapTrigger.setSerializationName("CSCGDV:Species Explorer In Map Trigger"); + _speciesExplorerInMapTrigger.setDisabled(true); + _statusColorAction.setSerializationName("CSCGDV:Status Color"); + _selectedGene.setDisabled(true); + _selectedGene.setString(""); + _createRowMultiSelectTree.setSerializationName("CSCGDV:Create Row MultiSelect Tree"); + _performGeneTableTsneAction.setSerializationName("CSCGDV:Perform Gene Table TSNE"); + _performGeneTableTsnePerplexity.setSerializationName("CSCGDV:Gene Table TSNE Perplexity"); + _performGeneTableTsnePerplexity.setMinimum(1); + _performGeneTableTsnePerplexity.setMaximum(50); + _performGeneTableTsnePerplexity.setValue(15); + _performGeneTableTsneKnn.setSerializationName("CSCGDV:Gene Table TSNE Knn"); + _performGeneTableTsneKnn.initialize({ "FLANN","HNSW","ANNOY" }, "ANNOY"); + _performGeneTableTsneDistance.setSerializationName("CSCGDV:Gene Table TSNE Distance"); + _performGeneTableTsneDistance.initialize({ "Euclidean","Cosine","Inner Product","Manhattan","Hamming","Dot" }, "Dot"); + _performGeneTableTsneTrigger.setSerializationName("CSCGDV:Gene Table TSNE Trigger"); + _performGeneTableTsneTrigger.setDisabled(true); + _clusterOrderHierarchy.setString(""); + _rightClickedCluster.setString(""); + _topSelectedHierarchyStatus.setString(""); + _tsnePerplexity.setSerializationName("CSCGDV:TSNE Perplexity"); + _tsnePerplexity.setMinimum(1); + _tsnePerplexity.setMaximum(50); + _tsnePerplexity.setValue(30); + _usePreComputedTSNE.setSerializationName("CSCGDV:Use Precomputed TSNE"); + _usePreComputedTSNE.setChecked(true); + _applyLogTransformation.setChecked(false); + _toggleScatterplotSelection.setChecked(false); + _performGeneTableTsneAction.setChecked(false); + _hiddenShowncolumns.setSerializationName("CSCGDV:Hidden Shown Columns"); + _speciesExplorerInMap.setSerializationName("CSCGDV:Species Explorer In Map"); + _topHierarchyClusterNamesFrequencyInclusionList.setSerializationName("CSCGDV:Top Hierarchy Cluster Names Frequency Inclusion List"); + _scatterplotReembedColorOption.setSerializationName("CSCGDV:Scatterplot Reembedding Color Option"); + _scatterplotEmbeddingPointsUMAPOption.setSerializationName("CSCGDV:Scatterplot Embedding UMAP Points Option"); + _typeofTopNGenes.setSerializationName("CSCGDV:Type of Top N Genes"); + _clusterCountSortingType.setSerializationName("CSCGDV:Cluster Count Sorting Type"); + _applyLogTransformation.setSerializationName("CSCGDV:Apply Log Transformation"); + _createRowMultiSelectTree.setDisabled(true); + _selectedRowIndex.setDisabled(true); + _selectedRowIndex.setString(""); + _scatterplotReembedColorOption.initialize({ "Species","Cluster","Expression" }, "Species"); + _typeofTopNGenes.initialize({ "Absolute","Negative","Positive" }, "Positive"); + _clusterCountSortingType.initialize({ "Count","Name","Hierarchy View" }, "Count"); + _topNGenesFilter.setDefaultWidgetFlags(IntegralAction::WidgetFlag::SpinBox); + + QIcon updateIcon = mv::util::StyledIcon("play"); + _startComputationTriggerAction.setIcon(updateIcon); + _startComputationTriggerAction.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); + + QIcon exploreIcon = mv::util::StyledIcon("wpexplorer"); + _speciesExplorerInMapTrigger.setIcon(exploreIcon); + _speciesExplorerInMapTrigger.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); + + QIcon removeIcon = mv::util::StyledIcon("backspace"); + _removeRowSelection.setIcon(removeIcon); + _removeRowSelection.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); + + + QIcon saveGeneTableIcon = mv::util::StyledIcon("save"); + _saveGeneTable.setIcon(saveGeneTableIcon); + _saveGeneTable.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); + + QIcon saveSpeciesTableIcon = mv::util::StyledIcon("save"); + _saveSpeciesTable.setIcon(saveSpeciesTableIcon); + _saveSpeciesTable.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); + + QIcon revertIcon = mv::util::StyledIcon("undo"); + _revertRowSelectionChangesToInitial.setIcon(revertIcon); + _revertRowSelectionChangesToInitial.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); + + _scatterplotEmbeddingPointsUMAPOption.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == PointType; + }); + _filteringEditTreeDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == CrossSpeciesComparisonTreeType; + }); + _referenceTreeDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == CrossSpeciesComparisonTreeType; + }); + _mainPointsDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == PointType; + }); + _speciesNamesDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == ClusterType; + }); + _bottomClusterNamesDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == ClusterType; + }); + _middleClusterNamesDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == ClusterType; + }); + _topClusterNamesDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == ClusterType; + }); + _embeddingDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == PointType; + }); + const auto changetooltipTopN = [this]() -> void + { + _topNGenesFilter.setToolTip("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setIconText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setToolTip("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setObjectName("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setWhatsThis("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + }; + connect(&_topNGenesFilter, &IntegralAction::valueChanged, this, changetooltipTopN); + + const auto updatespeciesExplorerInMap = [this]() -> void + { + + QStringList leafValues = _speciesExplorerInMap.getSelectedOptions(); + + + removeSelectionTableRows(&leafValues); + enableDisableButtonsAutomatically(); + }; + connect(&_speciesExplorerInMap, &OptionsAction::selectedOptionsChanged, this, updatespeciesExplorerInMap); + + const int delayMs = 500; // Delay in milliseconds + /* + QTimer* bottomTimer = new QTimer(this); + bottomTimer->setSingleShot(true); + const auto updateBottomHierarchyClusterNamesFrequencyInclusionList = [this, bottomTimer, delayMs]() -> void + { + bottomTimer->start(delayMs); + }; + connect(bottomTimer, &QTimer::timeout, this, [this]() { + //computeFrequencyMapForHierarchyItemsChange("bottom"); + computeHierarchyAppearanceVector("bottom"); + _statusColorAction.setString("M"); + }); + connect(&_bottomHierarchyClusterNamesFrequencyInclusionList, &OptionsAction::selectedOptionsChanged, this, updateBottomHierarchyClusterNamesFrequencyInclusionList); + + QTimer* middleTimer = new QTimer(this); + middleTimer->setSingleShot(true); + const auto updateMiddleHierarchyClusterNamesFrequencyInclusionList = [this, middleTimer, delayMs]() -> void + { + middleTimer->start(delayMs); + }; + connect(middleTimer, &QTimer::timeout, this, [this]() { + //computeFrequencyMapForHierarchyItemsChange("middle"); + computeHierarchyAppearanceVector("middle"); + _statusColorAction.setString("M"); + }); + connect(&_middleHierarchyClusterNamesFrequencyInclusionList, &OptionsAction::selectedOptionsChanged, this, updateMiddleHierarchyClusterNamesFrequencyInclusionList); + */ + QTimer* topTimer = new QTimer(this); + topTimer->setSingleShot(true); + const auto updateTopHierarchyClusterNamesFrequencyInclusionList = [this, topTimer, delayMs]() -> void + { + topTimer->start(delayMs); + }; + connect(topTimer, &QTimer::timeout, this, [this]() { + //computeFrequencyMapForHierarchyItemsChange("top"); + _statusColorAction.setString("M"); + }); + connect(&_topHierarchyClusterNamesFrequencyInclusionList, &OptionsAction::selectedOptionsChanged, this, updateTopHierarchyClusterNamesFrequencyInclusionList); + + const auto updateGeneFilteringTrigger = [this]() -> void + { + disableActions(); + _pauseStatusUpdates = true; + _speciesExplorerInMap.setSelectedOptions(QStringList{}); + //_erroredOutFlag = false; + QApplication::processEvents(); + updateButtonTriggered(); + enableActions(); + + QApplication::processEvents(); + auto pointsDataset = _mainPointsDataset.getCurrentDataset(); + pointsDataset->setSelectionIndices(_selectedIndicesFromStorage); + _statusColorAction.setString("C"); + + }; + connect(&_startComputationTriggerAction, &TriggerAction::triggered, this, updateGeneFilteringTrigger); + const auto updateCreateRowMultiSelectTreeTrigger = [this]() -> void { + + if (_filteringEditTreeDataset.getCurrentDataset().isValid()) + { + auto treeDataset = mv::data().getDataset(_filteringEditTreeDataset.getCurrentDataset().getDatasetId()); + + QStringList selectedRowsStrList = _geneNamesConnection.getString().split("*%$@*@$%*"); + + + if (treeDataset.isValid() && selectedRowsStrList.size() > 0) + { + //if (speciesSelectedIndicesCounter.size() > 0) + { + //QJsonObject valueStringReference = createJsonTree(speciesSelectedIndicesCounter); + //if (!valueStringReference.isEmpty()) + { + //treeDataset->setTreeData(valueStringReference); + //events().notifyDatasetDataChanged(treeDataset); + //TODO:: add the tree to the tree dataset +/* + QString treeData = createJsonTreeFromNewick(QString::fromStdString(modifiedNewick), leafnames); + if (!treeData.isEmpty()) + { + + QJsonObject valueStringReference = QJsonDocument::fromJson(treeData.toUtf8()).object(); + if (!valueStringReference.isEmpty()) + { + treeDataset->setTreeData(valueStringReference); + events().notifyDatasetDataChanged(treeDataset); + } + } + */ + + } + } + + + } + + + + + } + + + }; + + connect(&_createRowMultiSelectTree, &TriggerAction::triggered, this, updateCreateRowMultiSelectTreeTrigger); + + const auto updateMainPointsDataset = [this]() -> void { + + if (!_meanMapComputed) + { + computeGeneMeanExpressionMap(); + } + + + if (_mainPointsDataset.getCurrentDataset().isValid()) + { + _totalGeneList.clear(); + auto fullDataset = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); + auto dimensions = fullDataset->getNumDimensions(); + _totalGeneList = fullDataset->getDimensionNames(); + if (dimensions > 0) { + _topNGenesFilter.setMinimum(1); + _topNGenesFilter.setMaximum(dimensions); + + _topNGenesFilter.setValue(std::min(10, static_cast(dimensions))); + } + else { + _topNGenesFilter.setMinimum(0); + _topNGenesFilter.setMaximum(0); + _topNGenesFilter.setValue(0); + } + + _topNGenesFilter.setToolTip("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setIconText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setObjectName("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setWhatsThis("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + const auto mainSelectionChanged = [this]() -> void { + _toggleScatterplotSelection.setChecked(true); + }; + connect(&fullDataset, &Dataset::dataSelectionChanged, this, mainSelectionChanged); + + } + else + { + _topNGenesFilter.setMinimum(0); + _topNGenesFilter.setMaximum(0); + _topNGenesFilter.setValue(0); + _topNGenesFilter.setToolTip("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setIconText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setObjectName("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + _topNGenesFilter.setWhatsThis("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); + + } + + }; + + connect(&_mainPointsDataset, &DatasetPickerAction::currentIndexChanged, this, updateMainPointsDataset); + + + const auto updateSpeciesNameDataset = [this]() -> void { + _selectedSpeciesCellCountMap.clear(); + QStringList speciesOptions = {}; + if (_speciesNamesDataset.getCurrentDataset().isValid()) + { + auto clusterFullDataset = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); + auto clusterValuesData = clusterFullDataset->getClusters(); + if (!clusterValuesData.isEmpty()) + { + for (auto clusters : clusterValuesData) + { + { + auto name = clusters.getName(); + auto color = clusters.getColor(); + _selectedSpeciesCellCountMap[name].color = color; + _selectedSpeciesCellCountMap[name].selectedCellsCount = 0; + _selectedSpeciesCellCountMap[name].nonSelectedCellsCount = 0; + speciesOptions.append(name); + } + + } + + } + + } + _speciesExplorerInMap.setOptions(speciesOptions); + + if (!_meanMapComputed) + { + computeGeneMeanExpressionMap(); + } + }; + + connect(&_speciesNamesDataset, &DatasetPickerAction::currentIndexChanged, this, updateSpeciesNameDataset); + const auto updateTopHierarchyDatasetChanged = [this]() -> void { + + if (_topClusterNamesDataset.getCurrentDataset().isValid()) + { + + auto clusterFullDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); + auto clusters = clusterFullDataset->getClusters(); + QStringList clusterNames = {}; + if (!clusters.isEmpty()) + { + for (auto cluster : clusters) + { + clusterNames.append(cluster.getName()); + } + } + _topHierarchyClusterNamesFrequencyInclusionList.setOptions(clusterNames); + QString removalString = "Non-Neuronal"; + //if removal string present remove it + if (clusterNames.contains(removalString)) + { + clusterNames.removeAll(removalString); + + } + _topHierarchyClusterNamesFrequencyInclusionList.setSelectedOptions(clusterNames); + } + else + { + _topHierarchyClusterNamesFrequencyInclusionList.setOptions({}); + } + computeHierarchyAppearanceVector(); + + }; + + connect(&_topClusterNamesDataset, &DatasetPickerAction::currentIndexChanged, this, updateTopHierarchyDatasetChanged); + + const auto updateMiddleHierarchyDatasetChanged = [this]() -> void { + + + }; + + connect(&_middleClusterNamesDataset, &DatasetPickerAction::currentIndexChanged, this, updateMiddleHierarchyDatasetChanged); + + const auto updateBottomHierarchyDatasetChanged = [this]() -> void { + + + }; + + connect(&_bottomClusterNamesDataset, &DatasetPickerAction::currentIndexChanged, this, updateBottomHierarchyDatasetChanged); + + const auto updateScatterplotColor = [this]() -> void { + auto selectedColorType = _scatterplotReembedColorOption.getCurrentText(); + if (selectedColorType != "") + { + auto scatterplotViewFactory = mv::plugins().getPluginFactory("Scatterplot View"); + mv::gui::DatasetPickerAction* colorDatasetPickerAction; + mv::gui::DatasetPickerAction* pointDatasetPickerAction; + mv::gui::ViewPluginSamplerAction* samplerActionAction; + if (scatterplotViewFactory) { + for (auto plugin : mv::plugins().getPluginsByFactory(scatterplotViewFactory)) { + if (plugin->getGuiName() == "Scatterplot Cell Selection Overview") { + pointDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Position")); + if (pointDatasetPickerAction) { + + + if (pointDatasetPickerAction->getCurrentDataset() == _selectedPointsTSNEDataset) { + colorDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Color")); + if (colorDatasetPickerAction) + { + + + + + auto selectedColorType = _scatterplotReembedColorOption.getCurrentText(); + if (selectedColorType != "") + { + auto legendViewFactory = mv::plugins().getPluginFactory("ChartLegend View"); + DatasetPickerAction* legendDatasetPickerAction; + StringAction* chartTitle; + //ColorAction* selectionColor; + //StringAction* selectionStringDelimiter; + //StringAction* selectionClustersString; + if (legendViewFactory) + { + for (auto legendPlugin : mv::plugins().getPluginsByFactory(legendViewFactory)) + { + if (legendPlugin->getGuiName() == "Legend View") + { + //legendPlugin->printChildren(); + legendDatasetPickerAction = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster dataset")); + chartTitle = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Chart Title")); + + //selectionColor = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Selection color")); + //selectionStringDelimiter = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Delimiter")); + //selectionClustersString = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster Selection string")); + } + } + } + + + + + + if (selectedColorType == "Cluster") + { + if (_bottomClusterNamesDataset.getCurrentDataset().isValid()) + { + colorDatasetPickerAction->setCurrentText(""); + colorDatasetPickerAction->setCurrentDataset(_bottomClusterNamesDataset.getCurrentDataset()); + if (legendDatasetPickerAction) + { + legendDatasetPickerAction->setCurrentDataset(_bottomClusterNamesDataset.getCurrentDataset()); + } + if (chartTitle) + { + chartTitle->setString("Cell types"); + } + /* + if (selectionColor) + { + selectionColor->setColor(QColor(53, 126, 199)); + } + + if (selectionStringDelimiter) + { + selectionStringDelimiter->setString(","); + } + + if (selectionClustersString) + { + selectionClustersString->setString(""); //TODO + } + */ + } + } + else if (selectedColorType == "Species") + { + if (_speciesNamesDataset.getCurrentDataset().isValid()) + { + colorDatasetPickerAction->setCurrentText(""); + colorDatasetPickerAction->setCurrentDataset(_speciesNamesDataset.getCurrentDataset()); + if (legendDatasetPickerAction) + { + legendDatasetPickerAction->setCurrentDataset(_speciesNamesDataset.getCurrentDataset()); + } + if (chartTitle) + { + chartTitle->setString("Species"); + } + /* + if (selectionColor) + { + selectionColor->setColor(QColor(53, 126, 199)); + } + + if (selectionStringDelimiter) + { + selectionStringDelimiter->setString(","); + } + + if (selectionClustersString) + { + selectionClustersString->setString(""); //TODO + } + */ + } + } + else if (selectedColorType == "Expression") + { + if (_tsneDatasetExpressionColors.isValid()) + { + colorDatasetPickerAction->setCurrentText(""); + colorDatasetPickerAction->setCurrentDataset(_tsneDatasetExpressionColors); + if (legendDatasetPickerAction) + { + legendDatasetPickerAction->setCurrentDataset(_tsneDatasetExpressionColors); + } + if (chartTitle) + { + chartTitle->setString("Gene expression"); + } + } + } + + + + } + + + + + + } + + samplerActionAction = plugin->findChildByPath("Sampler"); + + if (samplerActionAction) + { + samplerActionAction->setHtmlViewGeneratorFunction([this](const ViewPluginSamplerAction::SampleContext& toolTipContext) -> QString { + QString clusterDatasetId = _speciesNamesDataset.getCurrentDataset().getDatasetId(); + return generateTooltip(toolTipContext, clusterDatasetId, true, "GlobalPointIndices"); + }); + } + } + } + } + } + } + + } + + }; + connect(&_scatterplotReembedColorOption, &OptionAction::currentIndexChanged, this, updateScatterplotColor); + + + const auto updateStatus = [this]() -> void { + if (_pauseStatusUpdates) + { + return; + } + auto string = _statusColorAction.getString(); + QString labelText = ""; + QString backgroundColor = "none"; + if (string == "C") + { + _startComputationTriggerAction.setDisabled(true); + //if (_popupMessageInit->isVisible()) + //{ + //_popupMessageInit->hide(); + //} + } + else + { + _startComputationTriggerAction.setDisabled(false); + } + if (string == "M") + { + _removeRowSelection.trigger(); + } + + if (string == "C") { + labelText = "Updated"; + backgroundColor = "#28a745"; // Green + + } + else if (string == "M") { + labelText = "Outdated"; + backgroundColor = "#ffc107"; // Gold + + } + else if (string == "E") { + labelText = "Error"; + backgroundColor = "#dc3545"; // Red + } + else if (string == "R") + { + labelText = "Processing"; + backgroundColor = "#007bff"; // Blue + } + else { + labelText = "Unknown"; + backgroundColor = "#6c757d"; // Grey + } + + + + + // Update the _statusBarActionWidget with the new label text and background color + _statusBarActionWidget->showMessage("Status: " + labelText); + _statusBarActionWidget->setStyleSheet("QStatusBar{padding-left:8px;background:" + backgroundColor + ";color:white;}"); + + + }; + connect(&_statusColorAction, &StringAction::stringChanged, this, updateStatus); + + /*const auto updateSelectedCellClusterInfoBox = [this]() -> void { + + + // Clear any previous message + _selectedCellClusterInfoStatusBar->clearMessage(); + + // Check if there's a previously added label and remove it + if (_currentCellSelectionClusterInfoLabel != nullptr) { + _selectedCellClusterInfoStatusBar->removeWidget(_currentCellSelectionClusterInfoLabel); + delete _currentCellSelectionClusterInfoLabel; // Delete the previous label to avoid memory leaks + _currentCellSelectionClusterInfoLabel = nullptr; // Reset the pointer to indicate there's no current label + } + + // Create a new QLabel + _currentCellSelectionClusterInfoLabel = new QLabel; + auto string = _selectedCellClusterInfoBox.getString(); + QString htmlText = string; + _currentCellSelectionClusterInfoLabel->setText(htmlText); + _selectedCellClusterInfoStatusBar->addWidget(_currentCellSelectionClusterInfoLabel); + + + QLayoutItem* layoutItem; + + while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { + delete layoutItem->widget(); + delete layoutItem; + } + + for (cluster : clusters) { + auto clusterLabel = new QLabel(parent, clusterName); + clusterLabel->setStyleSheet(""); + _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); + } + + + + };*/ + + const auto updateEmbeddingDataset = [this]() -> void { + + + }; + connect(&_embeddingDataset, &DatasetPickerAction::currentIndexChanged, this, updateEmbeddingDataset); + + + const auto updateTypeOfTopNGenesFilter = [this]() -> void { + _statusColorAction.setString("M"); + + }; + connect(&_typeofTopNGenes, &OptionAction::currentIndexChanged, this, updateTypeOfTopNGenesFilter); + + + const auto updateClusterOrderHierarchy = [this]() -> void { + + _customOrderClustersFromHierarchy.clear(); + if (_clusterOrderHierarchy.getString() != "") + { + QStringList clusterOrderHierarchyList = _clusterOrderHierarchy.getString().split(" @%$,$%@ "); + for (auto clusterOrderHierarchyItem : clusterOrderHierarchyList) + { + + _customOrderClustersFromHierarchy.push_back(clusterOrderHierarchyItem); + } + } + + + + + }; + connect(&_clusterOrderHierarchy, &StringAction::stringChanged, this, updateClusterOrderHierarchy); + + const auto updateRightClickedCluster = [this]() -> void { + + + //qDebug() << "Cluster Name and Level: " << _rightClickedCluster.getString(); + QString orderedClusters = _rightClickedCluster.getString(); + auto geneName = _selectedGene.getString(); + if (orderedClusters == "" || geneName == "") + { + _clearRightClickedCluster.trigger(); + //qDebug() << "Strings Empty, orderedClusters, genename" << orderedClusters << geneName; + return; + } + QStringList clusterNameAndLevel = orderedClusters.split(" @%$,$%@ "); + if (clusterNameAndLevel.size() == 2) + { + QString clusterName = clusterNameAndLevel.at(0); + QString clusterLevelTemp = clusterNameAndLevel.at(1); + if (clusterName == "" || clusterLevelTemp == "") + { + _clearRightClickedCluster.trigger(); + //qDebug() << "Strings Empty clustername, clusterLevelTemp" << clusterName << clusterLevelTemp; + return; + } + QString clusterLevel; + if (clusterLevelTemp == "1") + { + clusterLevel = "top"; + } + else if (clusterLevelTemp == "2") + { + clusterLevel = "middle"; + } + else if (clusterLevelTemp == "3") + { + clusterLevel = "bottom"; + } + else + { + + _clearRightClickedCluster.trigger(); + //qDebug() << "Cluster Level not 1,2,3" << clusterLevelTemp; + return; + + } + + //qDebug() << "Cluster Name: " << clusterName << " Cluster Level: " << clusterLevel; + + auto referenceTreeDataset = _referenceTreeDataset.getCurrentDataset(); + if (referenceTreeDataset.isValid()) { + auto referenceTree = mv::data().getDataset(referenceTreeDataset.getDatasetId()); + if (referenceTree.isValid()) { + QString speciesData = _precomputedTreesFromTheHierarchy[clusterLevel][clusterName][geneName]; + QJsonObject speciesDataJson = QJsonDocument::fromJson(speciesData.toUtf8()).object(); + //check if QJsonObject isValid + if (speciesDataJson.isEmpty()) + { + _clearRightClickedCluster.trigger(); + //qDebug() << "Species Data Json Empty"; + return; + } + referenceTree->setTreeData(speciesDataJson); + events().notifyDatasetDataChanged(referenceTree); + } + else + { + _clearRightClickedCluster.trigger(); + qDebug() << "Reference Tree Invalid"; + return; + } + + } + else + { + _clearRightClickedCluster.trigger(); + qDebug() << "Reference Tree Dataset Invalid"; + return; + } + + } + + }; + connect(&_rightClickedCluster, &StringAction::stringChanged, this, updateRightClickedCluster); + const auto updateTopSelectedHierarchyStatus = [this]() -> void { + + + + }; + connect(&_topSelectedHierarchyStatus, &StringAction::stringChanged, this, updateTopSelectedHierarchyStatus); + + const auto updateApplyLogTransformation = [this]() -> void { + _statusColorAction.setString("M"); + + }; + connect(&_applyLogTransformation, &ToggleAction::toggled, this, updateApplyLogTransformation); + const auto updateMapForHierarchyItemsChangeMethodStopForProjectLoadBlocker = [this]() -> void { + if (!_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) + { + // _startComputationTriggerAction.setDisabled(false); + //computeFrequencyMapForHierarchyItemsChange("top"); + + //_startComputationTriggerAction.trigger(); + + //QFuture future = QtConcurrent::run([this]() { computeFrequencyMapForHierarchyItemsChange("top"); }); + QFuture future1 = QtConcurrent::run([this]() { computeGeneMeanExpressionMap(); }); + QFuture future2 = QtConcurrent::run([this]() { computeHierarchyAppearanceVector(); }); + + //future.waitForFinished(); + + future1.waitForFinished(); + future2.waitForFinished(); + _startComputationTriggerAction.trigger(); + + /* + + _popupMessageInit->show(); + QApplication::processEvents(); + try { + QFuture future1 = QtConcurrent::run([this]() { computeGeneMeanExpressionMap(); }); + QFuture future2 = QtConcurrent::run([this]() { computeFrequencyMapForHierarchyItemsChange("top"); }); + future1.waitForFinished(); + future2.waitForFinished(); + } + catch (const std::exception& e) { + std::cerr << "Error during computation: " << e.what() << std::endl; + _popupMessageInit->hide(); + QApplication::processEvents(); + return; + } + + try { + QFuture future3 = QtConcurrent::run([this]() { precomputeTreesFromHierarchy(); }); + QFuture future4 = QtConcurrent::run([this]() { _startComputationTriggerAction.trigger(); }); + //future3.waitForFinished(); + future4.waitForFinished(); + } + catch (const std::exception& e) { + std::cerr << "Error during tree precomputation: " << e.what() << std::endl; + _popupMessageInit->hide(); + QApplication::processEvents(); + return; + } + + _popupMessageInit->hide(); + QApplication::processEvents(); + */ + } + else + { + _startComputationTriggerAction.setDisabled(true); + } + + }; + connect(&_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker, &ToggleAction::toggled, this, updateMapForHierarchyItemsChangeMethodStopForProjectLoadBlocker); + const auto updateToggleScatterplotSelection = [this]() -> void { + + auto scatterplotViewFactory = mv::plugins().getPluginFactory("Scatterplot View"); + mv::gui::DecimalAction* overlayopacityAction; + mv::gui::DecimalAction* overlayscaleAction; + + if (scatterplotViewFactory) { + for (auto plugin : mv::plugins().getPluginsByFactory(scatterplotViewFactory)) { + if (plugin->getGuiName() == "Scatterplot Embedding View") { + + overlayopacityAction = dynamic_cast(plugin->findChildByPath("Settings/Selection/Opacity")); + if (overlayopacityAction) + { + //qDebug() << "Overlay opacity action found"; + if (_toggleScatterplotSelection.isChecked()) + { + overlayopacityAction->setValue(100.0); + } + else + { + overlayopacityAction->setValue(0.0); + } + } + overlayscaleAction = dynamic_cast(plugin->findChildByPath("Settings/Selection/Scale")); + if (overlayscaleAction) + { + //qDebug() << "Overlay opacity action found"; + if (_toggleScatterplotSelection.isChecked()) + { + overlayscaleAction->setValue(200.0); + } + else + { + overlayscaleAction->setValue(100.0); + } + } + + } + } + } + + }; + connect(&_toggleScatterplotSelection, &ToggleAction::toggled, this, updateToggleScatterplotSelection); + + + const auto recomputeGeneTableTSNE = [this]() -> void { + if (_selectedPointsTSNEDatasetForGeneTable.isValid()) + { + + auto runningAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Running")); + + if (runningAction) + { + + if (runningAction->isChecked()) + { + auto stopAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Stop")); + if (stopAction) + { + stopAction->trigger(); + QApplication::processEvents(); + } + } + + } + + + auto startAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Start")); + if (startAction) { + + startAction->trigger(); + } + + } + + }; + connect(&_performGeneTableTsneTrigger, &TriggerAction::triggered, this, recomputeGeneTableTSNE); + + const auto triggerSaveGeneTable = [this]() -> void { + + exportTableViewToCSVPerGene(_geneTableView); + + /*if (_selectedGene.getString() != "") + { + exportTableViewToCSVPerGene(_geneTableView); + } + else + { + exportTableViewToCSV(_geneTableView);_selectionDetailsTable + }*/ + + }; + connect(&_saveGeneTable, &TriggerAction::triggered, this, triggerSaveGeneTable); + + const auto triggerSaveSpeciesTable = [this]() -> void { + + + exportTableViewToCSVForGenes(_geneTableView); + + + }; + connect(&_saveSpeciesTable, &TriggerAction::triggered, this, triggerSaveSpeciesTable); + + + const auto updateComputeTreesToDisplayFromHierarchy = [this]() -> void { + + _computeTreesToDisplayFromHierarchy.setDisabled(true); + precomputeTreesFromHierarchy(); + _computeTreesToDisplayFromHierarchy.setDisabled(false); + + }; + + connect(&_computeTreesToDisplayFromHierarchy, &TriggerAction::triggered, this, updateComputeTreesToDisplayFromHierarchy); + + const auto updateGeneTableTSNECheck = [this]() -> void { + _statusColorAction.setString("M"); + + }; + connect(&_performGeneTableTsneAction, &ToggleAction::toggled, this, updateGeneTableTSNECheck); + const auto updateClusterCountSortingType = [this]() -> void { + updateClusterInfoStatusBar(); + + }; + connect(&_clusterCountSortingType, &OptionAction::currentIndexChanged, this, updateClusterCountSortingType); + QTimer* debounceTimer = new QTimer(this); + debounceTimer->setSingleShot(true); + debounceTimer->setInterval(500); // 500 milliseconds wait time + + const auto debouncelambda = [this]() -> void { // Capture debounceTimer by + _statusColorAction.setString("M"); + disableActions(); + + findTopNGenesPerCluster(); + if (_projectOpened) + { + _statusColorAction.setString("C"); + } + + enableActions(); + }; + + connect(debounceTimer, &QTimer::timeout, this, debouncelambda); + + const auto updateTopGenesSlider = [this, debounceTimer]() -> void { // Capture debounceTimer by reference + //wait to see if any more updates are coming then call findTopNGenesPerCluster(); + // Restart the timer every time the value changes + debounceTimer->start(); + }; + connect(&_topNGenesFilter, &IntegralAction::valueChanged, this, updateTopGenesSlider); + + + +} diff --git a/src/SettingsAction.cpp b/src/SettingsAction.cpp index cfbb85b..334bc3c 100644 --- a/src/SettingsAction.cpp +++ b/src/SettingsAction.cpp @@ -214,4583 +214,100 @@ int findIndex(const std::vector& vec, int value) { return (it != vec.end()) ? static_cast(std::distance(vec.begin(), it)) : -1; } +namespace { -SettingsAction::SettingsAction(CrossSpeciesComparisonGeneDetectPlugin& CrossSpeciesComparisonGeneDetectPlugin) : - WidgetAction(&CrossSpeciesComparisonGeneDetectPlugin, "CrossSpeciesComparisonGeneDetectPlugin Settings"), - _crossSpeciesComparisonGeneDetectPlugin(CrossSpeciesComparisonGeneDetectPlugin), - _listModel(this, "List Model"), - _selectedGene(this, "Selected Gene"), - _filteringEditTreeDataset(this, "Filtering Tree Dataset"), - _selectedRowIndex(this, "Selected Row Index"), - _optionSelectionAction(*this), - _startComputationTriggerAction(this, "Update"), - _referenceTreeDataset(this, "Reference Tree Dataset"), - _mainPointsDataset(this, "Main Points Dataset"), - _embeddingDataset(this, "Embedding Dataset"), - //_hierarchyBottomClusterDataset(this, "Hierarchy Bottom Cluster Dataset"), - //_hierarchyMiddleClusterDataset(this, "Hierarchy Middle Cluster Dataset"), - //_hierarchyTopClusterDataset(this, "Hierarchy Top Cluster Dataset"), - _speciesNamesDataset(this, "Species Names"), - _bottomClusterNamesDataset(this, "Bottom Cluster Names"), - _middleClusterNamesDataset(this, "Middle Cluster Names"), - _topClusterNamesDataset(this, "Top Cluster Names"), - //_calculationReferenceCluster(this, "Calculation Reference Cluster"), - _filteredGeneNamesVariant(this, "Filtered Gene Names"), - _topNGenesFilter(this, "Top N"), - _geneNamesConnection(this, "Gene Names Connection"), - _createRowMultiSelectTree(this, "Create Row MultiSelect Tree"), - _performGeneTableTsneAction(this, "Perform Gene Table TSNE"), - _tsnePerplexity(this, "TSNE Perplexity"), - _hiddenShowncolumns(this, "Hidden Shown Columns"), - _scatterplotReembedColorOption(this, "Embed Color"), - _scatterplotEmbeddingPointsUMAPOption(this, "Embedding UMAP Points"), - _selectedSpeciesVals(this, "Selected Species Vals"), - _removeRowSelection(this, "DeSelect"), - _revertRowSelectionChangesToInitial(this, "Revert"), - _statusColorAction(this, "Status color"), - _typeofTopNGenes(this, "N Type"), - _usePreComputedTSNE(this, "Use Precomputed TSNE"), - _speciesExplorerInMap(this, "Leaves Explorer Options"), - _topHierarchyClusterNamesFrequencyInclusionList(this, "Top Hierarchy Cluster Names Frequency Inclusion List"), - _speciesExplorerInMapTrigger(this, "Explore"), - _applyLogTransformation(this, "Gene mapping log"), - _clusterCountSortingType(this, "Cluster Count Sorting Type"), - _currentCellSelectionClusterInfoLabel(nullptr), - _performGeneTableTsnePerplexity(this, "Perform Gene Table TSNE Perplexity"), - _performGeneTableTsneKnn(this, "Perform Gene Table TSNE Knn"), - _performGeneTableTsneDistance(this, "Perform Gene Table TSNE Distance"), - _performGeneTableTsneTrigger(this, "Perform Gene Table TSNE Trigger"), - _computeTreesToDisplayFromHierarchy(this, "Compute Trees To Display From Hierarchy"), - _clusterOrderHierarchy(this, "Cluster Order Hierarchy"), - _rightClickedCluster(this, "Right Clicked Cluster"), - _topSelectedHierarchyStatus(this, "Top Selected Hierarchy Status"), - _clearRightClickedCluster(this, "Clear Right Clicked Cluster"), - _toggleScatterplotSelection(this, "Show Scatterplot Selection"), - _mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker(this, "Map For Hierarchy Items Change Method Stop For Project Load Blocker"), - _saveSpeciesTable(this, "Save Left Gene Table"), - _saveGeneTable(this, "Save Right Species Selection Table") -{ - _mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.setChecked(true); - setSerializationName("CSCGDV:CrossSpeciesComparison Gene Detect Plugin Settings"); - _statusBarActionWidget = new QStatusBar(); +constexpr int kGeneChunkSize = 256; - - _popupMessageInit = new QMessageBox(); - _popupMessageInit->setIcon(QMessageBox::Information); - _popupMessageInit->setWindowTitle("Computation in Progress"); - _popupMessageInit->setText("Data Precomputation in Progress"); - _popupMessageInit->setInformativeText( - "The system is currently precomputing essential data to enhance your interactive exploration experience. " - "This process may take some time based on the input data size, your memory and processor.
This popup will close automatically once initialization is complete. " - "Thank you for using Cytosplore EvoViewer.

" - "Please visit our website https://viewer.cytosplore.org/ to learn more." - ); - _popupMessageInit->setTextFormat(Qt::RichText); // Enable rich text formatting - _popupMessageInit->setTextInteractionFlags(Qt::TextBrowserInteraction); // Enable link interaction - _popupMessageInit->setStandardButtons(QMessageBox::NoButton); - _popupMessageInit->setModal(true); - - // Create a message box to notify the user about the completion of the tree creation process - _popupMessageTreeCreationCompletion = new QMessageBox(); - _popupMessageTreeCreationCompletion->setIcon(QMessageBox::Information); - _popupMessageTreeCreationCompletion->setWindowTitle("Tree Creation Completed"); - _popupMessageTreeCreationCompletion->setText( - "The precomputed tree method has been completed. " - "Right-click is available in the hierarchy view. " - "To explore expression values across species for each cluster in the phylogenetic tree, " - ); - _popupMessageTreeCreationCompletion->setInformativeText( - "Right Click available in the cluster hierarchy view." - ); - _popupMessageTreeCreationCompletion->setStandardButtons(QMessageBox::Ok); - _popupMessageTreeCreationCompletion->setModal(false); - /* - _popupMessageInit = new QMessageBox(); - _popupMessageInit->setIcon(QMessageBox::Information); - _popupMessageInit->setWindowTitle("Computation in Progress"); - _popupMessageInit->setText( - "
" - "

Data Precomputation in Progress

" - "
" - ); - _popupMessageInit->setInformativeText( - "
" - "The system is currently precomputing essential data to enhance your interactive exploration experience. " - "This process may take some time based on your data size and processor. The popup will close automatically " - "once initialization is complete. Thank you for using Cytosplore EvoViewer." - "
" - ); - _popupMessageInit->setStandardButtons(QMessageBox::NoButton); - _popupMessageInit->setModal(true); - */ - - _searchBox = new CustomLineEdit(); - QIcon searchIcon = mv::util::StyledIcon("search"); - QAction* searchAction = new QAction(_searchBox); - searchAction->setIcon(searchIcon); - _searchBox->addAction(searchAction, QLineEdit::LeadingPosition); - _searchBox->setPlaceholderText("Search ID..."); - _searchBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - _searchBox->setMaximumHeight(22); - - //_searchBox->setMinimumWidth(100); - _searchBox->setMaximumWidth(800); - _searchBox->setAutoFillBackground(true); - _searchBox->setStyleSheet("QLineEdit { background-color: white; }"); - _searchBox->setClearButtonEnabled(false); - _searchBox->setFocusPolicy(Qt::StrongFocus); - _meanMapComputed = false; - _statusBarActionWidget->setStatusTip("Status"); - _statusBarActionWidget->setMaximumHeight(22); - //_statusBarActionWidget->setFixedWidth(120); - //_statusBarActionWidget->setMinimumWidth(100); - _statusBarActionWidget->setMaximumWidth(800); - _statusBarActionWidget->setAutoFillBackground(true); - _statusBarActionWidget->setSizeGripEnabled(false); - - _geneTableView = new QTableView(); - _selectionDetailsTable = new QTableView(); - - QPalette palette = _geneTableView->palette(); - palette.setColor(QPalette::Base, Qt::white); // Background color - palette.setColor(QPalette::Text, Qt::black); // Text color - - _geneTableView->setPalette(palette); - _selectionDetailsTable->setPalette(palette); - - - _splitter = new QHBoxLayout(); - _geneTableView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - _geneTableView->setSelectionBehavior(QAbstractItemView::SelectRows); - _geneTableView->setSelectionMode(QAbstractItemView::SingleSelection); - _geneTableView->setEditTriggers(QAbstractItemView::NoEditTriggers); - _geneTableView->setAlternatingRowColors(false); - _geneTableView->setSortingEnabled(true); - _geneTableView->setShowGrid(true); - _geneTableView->setGridStyle(Qt::SolidLine); - _geneTableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - _geneTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - _geneTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - _geneTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - _geneTableView->setCornerButtonEnabled(false); - _geneTableView->setWordWrap(false); - _geneTableView->setTabKeyNavigation(false); - _geneTableView->setAcceptDrops(false); - _geneTableView->setDropIndicatorShown(false); - _geneTableView->setDragEnabled(false); - _geneTableView->setDragDropMode(QAbstractItemView::NoDragDrop); - _geneTableView->setDragDropOverwriteMode(false); - _geneTableView->setAutoScroll(false); - _geneTableView->setAutoScrollMargin(16); - _geneTableView->setAutoFillBackground(true); - _geneTableView->setFrameShape(QFrame::NoFrame); - _geneTableView->setFrameShadow(QFrame::Plain); - _geneTableView->setLineWidth(0); - _geneTableView->setMidLineWidth(0); - _geneTableView->setFocusPolicy(Qt::NoFocus); - _geneTableView->setContextMenuPolicy(Qt::NoContextMenu); - _geneTableView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - _geneTableView->setMinimumSize(QSize(0, 0)); - _geneTableView->setMaximumSize(QSize(16777215, 16777215)); - _geneTableView->setBaseSize(QSize(0, 0)); - _geneTableView->setFocusPolicy(Qt::StrongFocus); - _geneTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - - //only highlight multiple rows if shiuft is pressed - _geneTableView->setSelectionBehavior(QAbstractItemView::SelectRows); - - // removeDatasets(groupIDDeletion); - //removeDatasets(groupId1); - //removeDatasets(groupId2); - //removeDatasets(groupId3); - - _selectionDetailsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - _selectionDetailsTable->setSelectionBehavior(QAbstractItemView::SelectRows); - _selectionDetailsTable->setSelectionMode(QAbstractItemView::SingleSelection); - _selectionDetailsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - _selectionDetailsTable->setAlternatingRowColors(false); - _selectionDetailsTable->setSortingEnabled(true); - _selectionDetailsTable->setShowGrid(true); - _selectionDetailsTable->setGridStyle(Qt::SolidLine); - _selectionDetailsTable->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - _selectionDetailsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - _selectionDetailsTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - _selectionDetailsTable->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - _selectionDetailsTable->setCornerButtonEnabled(false); - _selectionDetailsTable->setWordWrap(false); - _selectionDetailsTable->setTabKeyNavigation(false); - _selectionDetailsTable->setAcceptDrops(false); - _selectionDetailsTable->setDropIndicatorShown(false); - _selectionDetailsTable->setDragEnabled(false); - _selectionDetailsTable->setDragDropMode(QAbstractItemView::NoDragDrop); - _selectionDetailsTable->setDragDropOverwriteMode(false); - _selectionDetailsTable->setAutoScroll(false); - _selectionDetailsTable->setAutoScrollMargin(16); - _selectionDetailsTable->setAutoFillBackground(true); - _selectionDetailsTable->setFrameShape(QFrame::NoFrame); - _selectionDetailsTable->setFrameShadow(QFrame::Plain); - _selectionDetailsTable->setLineWidth(0); - _selectionDetailsTable->setMidLineWidth(0); - _selectionDetailsTable->setFocusPolicy(Qt::NoFocus); - _selectionDetailsTable->setContextMenuPolicy(Qt::NoContextMenu); - _selectionDetailsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - _selectionDetailsTable->setMinimumSize(QSize(0, 0)); - _selectionDetailsTable->setMaximumSize(QSize(16777215, 16777215)); - _selectionDetailsTable->setBaseSize(QSize(0, 0)); - _selectionDetailsTable->setFocusPolicy(Qt::StrongFocus); - _selectionDetailsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - _selectionDetailsTable->setSelectionBehavior(QAbstractItemView::SelectRows); - - - - _selectedCellClusterInfoStatusBar = new mv::gui::FlowLayout(); - - - _listModel.setSerializationName("CSCGDV:List Model"); - _selectedGene.setSerializationName("CSCGDV:Selected Gene"); - _mainPointsDataset.setSerializationName("CSCGDV:Main Points Dataset"); - _embeddingDataset.setSerializationName("CSCGDV:Embedding Dataset"); - _speciesNamesDataset.setSerializationName("CSCGDV:Species Names Dataset"); - _bottomClusterNamesDataset.setSerializationName("CSCGDV:Cluster Names Dataset"); - _middleClusterNamesDataset.setSerializationName("CSCGDV:Middle Cluster Names Dataset"); - _topClusterNamesDataset.setSerializationName("CSCGDV:Top Cluster Names Dataset"); - _filteredGeneNamesVariant.setSerializationName("CSCGDV:Filtered Gene Names"); - _topNGenesFilter.setSerializationName("CSCGDV:Top N Genes Filter"); - _filteringEditTreeDataset.setSerializationName("CSCGDV:Filtering Tree Dataset"); - _referenceTreeDataset.setSerializationName("CSCGDV:Reference Tree Dataset"); - _selectedRowIndex.setSerializationName("CSCGDV:Selected Row Index"); - _geneNamesConnection.setSerializationName("CSCGDV:Gene Names Connection"); - _selectedSpeciesVals.setSerializationName("CSCGDV:Selected Species Vals"); - _clusterOrderHierarchy.setSerializationName("CSCGDV:Cluster Order Hierarchy"); - _rightClickedCluster.setSerializationName("CSCGDV:Right Clicked Cluster"); - _topSelectedHierarchyStatus.setSerializationName("CSCGDV:Top Selected Hierarchy Status"); - _clearRightClickedCluster.setSerializationName("CSCGDV:Clear Right Clicked Cluster"); - _removeRowSelection.setSerializationName("CSCGDV:Remove Row Selection"); - _removeRowSelection.setDisabled(true); - _revertRowSelectionChangesToInitial.setSerializationName("CSCGDV:Revert Row Selection Changes To Initial"); - _revertRowSelectionChangesToInitial.setDisabled(true); - _speciesExplorerInMapTrigger.setSerializationName("CSCGDV:Species Explorer In Map Trigger"); - _speciesExplorerInMapTrigger.setDisabled(true); - _statusColorAction.setSerializationName("CSCGDV:Status Color"); - _selectedGene.setDisabled(true); - _selectedGene.setString(""); - _createRowMultiSelectTree.setSerializationName("CSCGDV:Create Row MultiSelect Tree"); - _performGeneTableTsneAction.setSerializationName("CSCGDV:Perform Gene Table TSNE"); - _performGeneTableTsnePerplexity.setSerializationName("CSCGDV:Gene Table TSNE Perplexity"); - _performGeneTableTsnePerplexity.setMinimum(1); - _performGeneTableTsnePerplexity.setMaximum(50); - _performGeneTableTsnePerplexity.setValue(15); - _performGeneTableTsneKnn.setSerializationName("CSCGDV:Gene Table TSNE Knn"); - _performGeneTableTsneKnn.initialize({ "FLANN","HNSW","ANNOY" }, "ANNOY"); - _performGeneTableTsneDistance.setSerializationName("CSCGDV:Gene Table TSNE Distance"); - _performGeneTableTsneDistance.initialize({ "Euclidean","Cosine","Inner Product","Manhattan","Hamming","Dot" }, "Dot"); - _performGeneTableTsneTrigger.setSerializationName("CSCGDV:Gene Table TSNE Trigger"); - _performGeneTableTsneTrigger.setDisabled(true); - _clusterOrderHierarchy.setString(""); - _rightClickedCluster.setString(""); - _topSelectedHierarchyStatus.setString(""); - _tsnePerplexity.setSerializationName("CSCGDV:TSNE Perplexity"); - _tsnePerplexity.setMinimum(1); - _tsnePerplexity.setMaximum(50); - _tsnePerplexity.setValue(30); - _usePreComputedTSNE.setSerializationName("CSCGDV:Use Precomputed TSNE"); - _usePreComputedTSNE.setChecked(true); - _applyLogTransformation.setChecked(false); - _toggleScatterplotSelection.setChecked(false); - _performGeneTableTsneAction.setChecked(false); - _hiddenShowncolumns.setSerializationName("CSCGDV:Hidden Shown Columns"); - _speciesExplorerInMap.setSerializationName("CSCGDV:Species Explorer In Map"); - _topHierarchyClusterNamesFrequencyInclusionList.setSerializationName("CSCGDV:Top Hierarchy Cluster Names Frequency Inclusion List"); - _scatterplotReembedColorOption.setSerializationName("CSCGDV:Scatterplot Reembedding Color Option"); - _scatterplotEmbeddingPointsUMAPOption.setSerializationName("CSCGDV:Scatterplot Embedding UMAP Points Option"); - _typeofTopNGenes.setSerializationName("CSCGDV:Type of Top N Genes"); - _clusterCountSortingType.setSerializationName("CSCGDV:Cluster Count Sorting Type"); - _applyLogTransformation.setSerializationName("CSCGDV:Apply Log Transformation"); - _createRowMultiSelectTree.setDisabled(true); - _selectedRowIndex.setDisabled(true); - _selectedRowIndex.setString(""); - _scatterplotReembedColorOption.initialize({ "Species","Cluster","Expression" }, "Species"); - _typeofTopNGenes.initialize({ "Absolute","Negative","Positive" }, "Positive"); - _clusterCountSortingType.initialize({ "Count","Name","Hierarchy View" }, "Count"); - _topNGenesFilter.setDefaultWidgetFlags(IntegralAction::WidgetFlag::SpinBox); - - QIcon updateIcon = mv::util::StyledIcon("play"); - _startComputationTriggerAction.setIcon(updateIcon); - _startComputationTriggerAction.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); - - QIcon exploreIcon = mv::util::StyledIcon("wpexplorer"); - _speciesExplorerInMapTrigger.setIcon(exploreIcon); - _speciesExplorerInMapTrigger.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); - - QIcon removeIcon = mv::util::StyledIcon("backspace"); - _removeRowSelection.setIcon(removeIcon); - _removeRowSelection.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); - - - QIcon saveGeneTableIcon = mv::util::StyledIcon("save"); - _saveGeneTable.setIcon(saveGeneTableIcon); - _saveGeneTable.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); - - QIcon saveSpeciesTableIcon = mv::util::StyledIcon("save"); - _saveSpeciesTable.setIcon(saveSpeciesTableIcon); - _saveSpeciesTable.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); - - QIcon revertIcon = mv::util::StyledIcon("undo"); - _revertRowSelectionChangesToInitial.setIcon(revertIcon); - _revertRowSelectionChangesToInitial.setDefaultWidgetFlags(TriggerAction::WidgetFlag::IconText); - - _scatterplotEmbeddingPointsUMAPOption.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == PointType; - }); - _filteringEditTreeDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == CrossSpeciesComparisonTreeType; - }); - _referenceTreeDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == CrossSpeciesComparisonTreeType; - }); - _mainPointsDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == PointType; - }); - _speciesNamesDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == ClusterType; - }); - _bottomClusterNamesDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == ClusterType; - }); - _middleClusterNamesDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == ClusterType; - }); - _topClusterNamesDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == ClusterType; - }); - _embeddingDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == PointType; - }); - const auto changetooltipTopN = [this]() -> void - { - _topNGenesFilter.setToolTip("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setIconText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setToolTip("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setObjectName("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setWhatsThis("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - }; - connect(&_topNGenesFilter, &IntegralAction::valueChanged, this, changetooltipTopN); - - const auto updatespeciesExplorerInMap = [this]() -> void - { - - QStringList leafValues = _speciesExplorerInMap.getSelectedOptions(); - - - removeSelectionTableRows(&leafValues); - enableDisableButtonsAutomatically(); - }; - connect(&_speciesExplorerInMap, &OptionsAction::selectedOptionsChanged, this, updatespeciesExplorerInMap); - - const int delayMs = 500; // Delay in milliseconds - /* - QTimer* bottomTimer = new QTimer(this); - bottomTimer->setSingleShot(true); - const auto updateBottomHierarchyClusterNamesFrequencyInclusionList = [this, bottomTimer, delayMs]() -> void - { - bottomTimer->start(delayMs); - }; - connect(bottomTimer, &QTimer::timeout, this, [this]() { - //computeFrequencyMapForHierarchyItemsChange("bottom"); - computeHierarchyAppearanceVector("bottom"); - _statusColorAction.setString("M"); - }); - connect(&_bottomHierarchyClusterNamesFrequencyInclusionList, &OptionsAction::selectedOptionsChanged, this, updateBottomHierarchyClusterNamesFrequencyInclusionList); - - QTimer* middleTimer = new QTimer(this); - middleTimer->setSingleShot(true); - const auto updateMiddleHierarchyClusterNamesFrequencyInclusionList = [this, middleTimer, delayMs]() -> void - { - middleTimer->start(delayMs); - }; - connect(middleTimer, &QTimer::timeout, this, [this]() { - //computeFrequencyMapForHierarchyItemsChange("middle"); - computeHierarchyAppearanceVector("middle"); - _statusColorAction.setString("M"); - }); - connect(&_middleHierarchyClusterNamesFrequencyInclusionList, &OptionsAction::selectedOptionsChanged, this, updateMiddleHierarchyClusterNamesFrequencyInclusionList); - */ - QTimer* topTimer = new QTimer(this); - topTimer->setSingleShot(true); - const auto updateTopHierarchyClusterNamesFrequencyInclusionList = [this, topTimer, delayMs]() -> void - { - topTimer->start(delayMs); - }; - connect(topTimer, &QTimer::timeout, this, [this]() { - //computeFrequencyMapForHierarchyItemsChange("top"); - _statusColorAction.setString("M"); - }); - connect(&_topHierarchyClusterNamesFrequencyInclusionList, &OptionsAction::selectedOptionsChanged, this, updateTopHierarchyClusterNamesFrequencyInclusionList); - - const auto updateGeneFilteringTrigger = [this]() -> void - { - disableActions(); - _pauseStatusUpdates = true; - _speciesExplorerInMap.setSelectedOptions(QStringList{}); - //_erroredOutFlag = false; - QApplication::processEvents(); - updateButtonTriggered(); - enableActions(); - - QApplication::processEvents(); - auto pointsDataset = _mainPointsDataset.getCurrentDataset(); - pointsDataset->setSelectionIndices(_selectedIndicesFromStorage); - _statusColorAction.setString("C"); - - }; - connect(&_startComputationTriggerAction, &TriggerAction::triggered, this, updateGeneFilteringTrigger); - const auto updateCreateRowMultiSelectTreeTrigger = [this]() -> void { - - if (_filteringEditTreeDataset.getCurrentDataset().isValid()) - { - auto treeDataset = mv::data().getDataset(_filteringEditTreeDataset.getCurrentDataset().getDatasetId()); - - QStringList selectedRowsStrList = _geneNamesConnection.getString().split("*%$@*@$%*"); - - - if (treeDataset.isValid() && selectedRowsStrList.size() > 0) - { - //if (speciesSelectedIndicesCounter.size() > 0) - { - //QJsonObject valueStringReference = createJsonTree(speciesSelectedIndicesCounter); - //if (!valueStringReference.isEmpty()) - { - //treeDataset->setTreeData(valueStringReference); - //events().notifyDatasetDataChanged(treeDataset); - //TODO:: add the tree to the tree dataset -/* - QString treeData = createJsonTreeFromNewick(QString::fromStdString(modifiedNewick), leafnames); - if (!treeData.isEmpty()) - { - - QJsonObject valueStringReference = QJsonDocument::fromJson(treeData.toUtf8()).object(); - if (!valueStringReference.isEmpty()) - { - treeDataset->setTreeData(valueStringReference); - events().notifyDatasetDataChanged(treeDataset); - } - } - */ - - } - } - - - } - - - - - } - - - }; - - connect(&_createRowMultiSelectTree, &TriggerAction::triggered, this, updateCreateRowMultiSelectTreeTrigger); - - const auto updateMainPointsDataset = [this]() -> void { - - if (!_meanMapComputed) - { - computeGeneMeanExpressionMap(); - } - - - if (_mainPointsDataset.getCurrentDataset().isValid()) - { - _totalGeneList.clear(); - auto fullDataset = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); - auto dimensions = fullDataset->getNumDimensions(); - _totalGeneList = fullDataset->getDimensionNames(); - if (dimensions > 0) { - _topNGenesFilter.setMinimum(1); - _topNGenesFilter.setMaximum(dimensions); - - _topNGenesFilter.setValue(std::min(10, static_cast(dimensions))); - } - else { - _topNGenesFilter.setMinimum(0); - _topNGenesFilter.setMaximum(0); - _topNGenesFilter.setValue(0); - } - - _topNGenesFilter.setToolTip("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setIconText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setObjectName("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setWhatsThis("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - const auto mainSelectionChanged = [this]() -> void { - _toggleScatterplotSelection.setChecked(true); - }; - connect(&fullDataset, &Dataset::dataSelectionChanged, this, mainSelectionChanged); - - } - else - { - _topNGenesFilter.setMinimum(0); - _topNGenesFilter.setMaximum(0); - _topNGenesFilter.setValue(0); - _topNGenesFilter.setToolTip("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setIconText("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setObjectName("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - _topNGenesFilter.setWhatsThis("Top N genes: 0 to " + QString::number(_topNGenesFilter.getMaximum()) + " (Current: " + QString::number(_topNGenesFilter.getValue()) + ")"); - - } - - }; - - connect(&_mainPointsDataset, &DatasetPickerAction::currentIndexChanged, this, updateMainPointsDataset); - - - const auto updateSpeciesNameDataset = [this]() -> void { - _selectedSpeciesCellCountMap.clear(); - QStringList speciesOptions = {}; - if (_speciesNamesDataset.getCurrentDataset().isValid()) - { - auto clusterFullDataset = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); - auto clusterValuesData = clusterFullDataset->getClusters(); - if (!clusterValuesData.isEmpty()) - { - for (auto clusters : clusterValuesData) - { - { - auto name = clusters.getName(); - auto color = clusters.getColor(); - _selectedSpeciesCellCountMap[name].color = color; - _selectedSpeciesCellCountMap[name].selectedCellsCount = 0; - _selectedSpeciesCellCountMap[name].nonSelectedCellsCount = 0; - speciesOptions.append(name); - } - - } - - } - - } - _speciesExplorerInMap.setOptions(speciesOptions); - - if (!_meanMapComputed) - { - computeGeneMeanExpressionMap(); - } - }; - - connect(&_speciesNamesDataset, &DatasetPickerAction::currentIndexChanged, this, updateSpeciesNameDataset); - const auto updateTopHierarchyDatasetChanged = [this]() -> void { - - if (_topClusterNamesDataset.getCurrentDataset().isValid()) - { - - auto clusterFullDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); - auto clusters = clusterFullDataset->getClusters(); - QStringList clusterNames = {}; - if (!clusters.isEmpty()) - { - for (auto cluster : clusters) - { - clusterNames.append(cluster.getName()); - } - } - _topHierarchyClusterNamesFrequencyInclusionList.setOptions(clusterNames); - QString removalString = "Non-Neuronal"; - //if removal string present remove it - if (clusterNames.contains(removalString)) - { - clusterNames.removeAll(removalString); - - } - _topHierarchyClusterNamesFrequencyInclusionList.setSelectedOptions(clusterNames); - } - else - { - _topHierarchyClusterNamesFrequencyInclusionList.setOptions({}); - } - computeHierarchyAppearanceVector(); - - }; - - connect(&_topClusterNamesDataset, &DatasetPickerAction::currentIndexChanged, this, updateTopHierarchyDatasetChanged); - - const auto updateMiddleHierarchyDatasetChanged = [this]() -> void { - - - }; - - connect(&_middleClusterNamesDataset, &DatasetPickerAction::currentIndexChanged, this, updateMiddleHierarchyDatasetChanged); - - const auto updateBottomHierarchyDatasetChanged = [this]() -> void { - - - }; - - connect(&_bottomClusterNamesDataset, &DatasetPickerAction::currentIndexChanged, this, updateBottomHierarchyDatasetChanged); - - const auto updateScatterplotColor = [this]() -> void { - auto selectedColorType = _scatterplotReembedColorOption.getCurrentText(); - if (selectedColorType != "") - { - auto scatterplotViewFactory = mv::plugins().getPluginFactory("Scatterplot View"); - mv::gui::DatasetPickerAction* colorDatasetPickerAction; - mv::gui::DatasetPickerAction* pointDatasetPickerAction; - mv::gui::ViewPluginSamplerAction* samplerActionAction; - if (scatterplotViewFactory) { - for (auto plugin : mv::plugins().getPluginsByFactory(scatterplotViewFactory)) { - if (plugin->getGuiName() == "Scatterplot Cell Selection Overview") { - pointDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Position")); - if (pointDatasetPickerAction) { - - - if (pointDatasetPickerAction->getCurrentDataset() == _selectedPointsTSNEDataset) { - colorDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Color")); - if (colorDatasetPickerAction) - { - - - - - auto selectedColorType = _scatterplotReembedColorOption.getCurrentText(); - if (selectedColorType != "") - { - auto legendViewFactory = mv::plugins().getPluginFactory("ChartLegend View"); - DatasetPickerAction* legendDatasetPickerAction; - StringAction* chartTitle; - //ColorAction* selectionColor; - //StringAction* selectionStringDelimiter; - //StringAction* selectionClustersString; - if (legendViewFactory) - { - for (auto legendPlugin : mv::plugins().getPluginsByFactory(legendViewFactory)) - { - if (legendPlugin->getGuiName() == "Legend View") - { - //legendPlugin->printChildren(); - legendDatasetPickerAction = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster dataset")); - chartTitle = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Chart Title")); - - //selectionColor = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Selection color")); - //selectionStringDelimiter = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Delimiter")); - //selectionClustersString = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster Selection string")); - } - } - } - - - - - - if (selectedColorType == "Cluster") - { - if (_bottomClusterNamesDataset.getCurrentDataset().isValid()) - { - colorDatasetPickerAction->setCurrentText(""); - colorDatasetPickerAction->setCurrentDataset(_bottomClusterNamesDataset.getCurrentDataset()); - if (legendDatasetPickerAction) - { - legendDatasetPickerAction->setCurrentDataset(_bottomClusterNamesDataset.getCurrentDataset()); - } - if (chartTitle) - { - chartTitle->setString("Cell types"); - } - /* - if (selectionColor) - { - selectionColor->setColor(QColor(53, 126, 199)); - } - - if (selectionStringDelimiter) - { - selectionStringDelimiter->setString(","); - } - - if (selectionClustersString) - { - selectionClustersString->setString(""); //TODO - } - */ - } - } - else if (selectedColorType == "Species") - { - if (_speciesNamesDataset.getCurrentDataset().isValid()) - { - colorDatasetPickerAction->setCurrentText(""); - colorDatasetPickerAction->setCurrentDataset(_speciesNamesDataset.getCurrentDataset()); - if (legendDatasetPickerAction) - { - legendDatasetPickerAction->setCurrentDataset(_speciesNamesDataset.getCurrentDataset()); - } - if (chartTitle) - { - chartTitle->setString("Species"); - } - /* - if (selectionColor) - { - selectionColor->setColor(QColor(53, 126, 199)); - } - - if (selectionStringDelimiter) - { - selectionStringDelimiter->setString(","); - } - - if (selectionClustersString) - { - selectionClustersString->setString(""); //TODO - } - */ - } - } - else if (selectedColorType == "Expression") - { - if (_tsneDatasetExpressionColors.isValid()) - { - colorDatasetPickerAction->setCurrentText(""); - colorDatasetPickerAction->setCurrentDataset(_tsneDatasetExpressionColors); - if (legendDatasetPickerAction) - { - legendDatasetPickerAction->setCurrentDataset(_tsneDatasetExpressionColors); - } - if (chartTitle) - { - chartTitle->setString("Gene expression"); - } - } - } - - - - } - - - - - - } - - samplerActionAction = plugin->findChildByPath("Sampler"); - - if (samplerActionAction) - { - samplerActionAction->setHtmlViewGeneratorFunction([this](const ViewPluginSamplerAction::SampleContext& toolTipContext) -> QString { - QString clusterDatasetId = _speciesNamesDataset.getCurrentDataset().getDatasetId(); - return generateTooltip(toolTipContext, clusterDatasetId, true, "GlobalPointIndices"); - }); - } - } - } - } - } - } - - } - - }; - connect(&_scatterplotReembedColorOption, &OptionAction::currentIndexChanged, this, updateScatterplotColor); - - - const auto updateStatus = [this]() -> void { - if (_pauseStatusUpdates) - { - return; - } - auto string = _statusColorAction.getString(); - QString labelText = ""; - QString backgroundColor = "none"; - if (string == "C") - { - _startComputationTriggerAction.setDisabled(true); - //if (_popupMessageInit->isVisible()) - //{ - //_popupMessageInit->hide(); - //} - } - else - { - _startComputationTriggerAction.setDisabled(false); - } - if (string == "M") - { - _removeRowSelection.trigger(); - } - - if (string == "C") { - labelText = "Updated"; - backgroundColor = "#28a745"; // Green - - } - else if (string == "M") { - labelText = "Outdated"; - backgroundColor = "#ffc107"; // Gold - - } - else if (string == "E") { - labelText = "Error"; - backgroundColor = "#dc3545"; // Red - } - else if (string == "R") - { - labelText = "Processing"; - backgroundColor = "#007bff"; // Blue - } - else { - labelText = "Unknown"; - backgroundColor = "#6c757d"; // Grey - } - - - - - // Update the _statusBarActionWidget with the new label text and background color - _statusBarActionWidget->showMessage("Status: " + labelText); - _statusBarActionWidget->setStyleSheet("QStatusBar{padding-left:8px;background:" + backgroundColor + ";color:white;}"); - - - }; - connect(&_statusColorAction, &StringAction::stringChanged, this, updateStatus); - - /*const auto updateSelectedCellClusterInfoBox = [this]() -> void { - - - // Clear any previous message - _selectedCellClusterInfoStatusBar->clearMessage(); - - // Check if there's a previously added label and remove it - if (_currentCellSelectionClusterInfoLabel != nullptr) { - _selectedCellClusterInfoStatusBar->removeWidget(_currentCellSelectionClusterInfoLabel); - delete _currentCellSelectionClusterInfoLabel; // Delete the previous label to avoid memory leaks - _currentCellSelectionClusterInfoLabel = nullptr; // Reset the pointer to indicate there's no current label - } - - // Create a new QLabel - _currentCellSelectionClusterInfoLabel = new QLabel; - auto string = _selectedCellClusterInfoBox.getString(); - QString htmlText = string; - _currentCellSelectionClusterInfoLabel->setText(htmlText); - _selectedCellClusterInfoStatusBar->addWidget(_currentCellSelectionClusterInfoLabel); - - - QLayoutItem* layoutItem; - - while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { - delete layoutItem->widget(); - delete layoutItem; - } - - for (cluster : clusters) { - auto clusterLabel = new QLabel(parent, clusterName); - clusterLabel->setStyleSheet(""); - _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); - } - - - - };*/ - - const auto updateEmbeddingDataset = [this]() -> void { - - - }; - connect(&_embeddingDataset, &DatasetPickerAction::currentIndexChanged, this, updateEmbeddingDataset); - - - const auto updateTypeOfTopNGenesFilter = [this]() -> void { - _statusColorAction.setString("M"); - - }; - connect(&_typeofTopNGenes, &OptionAction::currentIndexChanged, this, updateTypeOfTopNGenesFilter); - - - const auto updateClusterOrderHierarchy = [this]() -> void { - - _customOrderClustersFromHierarchy.clear(); - if (_clusterOrderHierarchy.getString() != "") - { - QStringList clusterOrderHierarchyList = _clusterOrderHierarchy.getString().split(" @%$,$%@ "); - for (auto clusterOrderHierarchyItem : clusterOrderHierarchyList) - { - - _customOrderClustersFromHierarchy.push_back(clusterOrderHierarchyItem); - } - } - - - - - }; - connect(&_clusterOrderHierarchy, &StringAction::stringChanged, this, updateClusterOrderHierarchy); - - const auto updateRightClickedCluster = [this]() -> void { - - - //qDebug() << "Cluster Name and Level: " << _rightClickedCluster.getString(); - QString orderedClusters = _rightClickedCluster.getString(); - auto geneName = _selectedGene.getString(); - if (orderedClusters == "" || geneName == "") - { - _clearRightClickedCluster.trigger(); - //qDebug() << "Strings Empty, orderedClusters, genename" << orderedClusters << geneName; - return; - } - QStringList clusterNameAndLevel = orderedClusters.split(" @%$,$%@ "); - if (clusterNameAndLevel.size() == 2) - { - QString clusterName = clusterNameAndLevel.at(0); - QString clusterLevelTemp = clusterNameAndLevel.at(1); - if (clusterName == "" || clusterLevelTemp == "") - { - _clearRightClickedCluster.trigger(); - //qDebug() << "Strings Empty clustername, clusterLevelTemp" << clusterName << clusterLevelTemp; - return; - } - QString clusterLevel; - if (clusterLevelTemp == "1") - { - clusterLevel = "top"; - } - else if (clusterLevelTemp == "2") - { - clusterLevel = "middle"; - } - else if (clusterLevelTemp == "3") - { - clusterLevel = "bottom"; - } - else - { - - _clearRightClickedCluster.trigger(); - //qDebug() << "Cluster Level not 1,2,3" << clusterLevelTemp; - return; - - } - - //qDebug() << "Cluster Name: " << clusterName << " Cluster Level: " << clusterLevel; - - auto referenceTreeDataset = _referenceTreeDataset.getCurrentDataset(); - if (referenceTreeDataset.isValid()) { - auto referenceTree = mv::data().getDataset(referenceTreeDataset.getDatasetId()); - if (referenceTree.isValid()) { - QString speciesData = _precomputedTreesFromTheHierarchy[clusterLevel][clusterName][geneName]; - QJsonObject speciesDataJson = QJsonDocument::fromJson(speciesData.toUtf8()).object(); - //check if QJsonObject isValid - if (speciesDataJson.isEmpty()) - { - _clearRightClickedCluster.trigger(); - //qDebug() << "Species Data Json Empty"; - return; - } - referenceTree->setTreeData(speciesDataJson); - events().notifyDatasetDataChanged(referenceTree); - } - else - { - _clearRightClickedCluster.trigger(); - qDebug() << "Reference Tree Invalid"; - return; - } - - } - else - { - _clearRightClickedCluster.trigger(); - qDebug() << "Reference Tree Dataset Invalid"; - return; - } - - } - - }; - connect(&_rightClickedCluster, &StringAction::stringChanged, this, updateRightClickedCluster); - const auto updateTopSelectedHierarchyStatus = [this]() -> void { - - - - }; - connect(&_topSelectedHierarchyStatus, &StringAction::stringChanged, this, updateTopSelectedHierarchyStatus); - - const auto updateApplyLogTransformation = [this]() -> void { - _statusColorAction.setString("M"); - - }; - connect(&_applyLogTransformation, &ToggleAction::toggled, this, updateApplyLogTransformation); - const auto updateMapForHierarchyItemsChangeMethodStopForProjectLoadBlocker = [this]() -> void { - if (!_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) - { - // _startComputationTriggerAction.setDisabled(false); - //computeFrequencyMapForHierarchyItemsChange("top"); - - //_startComputationTriggerAction.trigger(); - - //QFuture future = QtConcurrent::run([this]() { computeFrequencyMapForHierarchyItemsChange("top"); }); - QFuture future1 = QtConcurrent::run([this]() { computeGeneMeanExpressionMap(); }); - QFuture future2 = QtConcurrent::run([this]() { computeHierarchyAppearanceVector(); }); - - //future.waitForFinished(); - - future1.waitForFinished(); - future2.waitForFinished(); - _startComputationTriggerAction.trigger(); - - /* - - _popupMessageInit->show(); - QApplication::processEvents(); - try { - QFuture future1 = QtConcurrent::run([this]() { computeGeneMeanExpressionMap(); }); - QFuture future2 = QtConcurrent::run([this]() { computeFrequencyMapForHierarchyItemsChange("top"); }); - future1.waitForFinished(); - future2.waitForFinished(); - } - catch (const std::exception& e) { - std::cerr << "Error during computation: " << e.what() << std::endl; - _popupMessageInit->hide(); - QApplication::processEvents(); - return; - } - - try { - QFuture future3 = QtConcurrent::run([this]() { precomputeTreesFromHierarchy(); }); - QFuture future4 = QtConcurrent::run([this]() { _startComputationTriggerAction.trigger(); }); - //future3.waitForFinished(); - future4.waitForFinished(); - } - catch (const std::exception& e) { - std::cerr << "Error during tree precomputation: " << e.what() << std::endl; - _popupMessageInit->hide(); - QApplication::processEvents(); - return; - } - - _popupMessageInit->hide(); - QApplication::processEvents(); - */ - } - else - { - _startComputationTriggerAction.setDisabled(true); - } - - }; - connect(&_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker, &ToggleAction::toggled, this, updateMapForHierarchyItemsChangeMethodStopForProjectLoadBlocker); - const auto updateToggleScatterplotSelection = [this]() -> void { - - auto scatterplotViewFactory = mv::plugins().getPluginFactory("Scatterplot View"); - mv::gui::DecimalAction* overlayopacityAction; - mv::gui::DecimalAction* overlayscaleAction; - - if (scatterplotViewFactory) { - for (auto plugin : mv::plugins().getPluginsByFactory(scatterplotViewFactory)) { - if (plugin->getGuiName() == "Scatterplot Embedding View") { - - overlayopacityAction = dynamic_cast(plugin->findChildByPath("Settings/Selection/Opacity")); - if (overlayopacityAction) - { - //qDebug() << "Overlay opacity action found"; - if (_toggleScatterplotSelection.isChecked()) - { - overlayopacityAction->setValue(100.0); - } - else - { - overlayopacityAction->setValue(0.0); - } - } - overlayscaleAction = dynamic_cast(plugin->findChildByPath("Settings/Selection/Scale")); - if (overlayscaleAction) - { - //qDebug() << "Overlay opacity action found"; - if (_toggleScatterplotSelection.isChecked()) - { - overlayscaleAction->setValue(200.0); - } - else - { - overlayscaleAction->setValue(100.0); - } - } - - } - } - } - - }; - connect(&_toggleScatterplotSelection, &ToggleAction::toggled, this, updateToggleScatterplotSelection); - - - const auto recomputeGeneTableTSNE = [this]() -> void { - if (_selectedPointsTSNEDatasetForGeneTable.isValid()) - { - - auto runningAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Running")); - - if (runningAction) - { - - if (runningAction->isChecked()) - { - auto stopAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Stop")); - if (stopAction) - { - stopAction->trigger(); - std::this_thread::sleep_for(std::chrono::seconds(5)); - } - } - - } - - - auto startAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Start")); - if (startAction) { - - startAction->trigger(); - } - - } - - }; - connect(&_performGeneTableTsneTrigger, &TriggerAction::triggered, this, recomputeGeneTableTSNE); - - const auto triggerSaveGeneTable = [this]() -> void { - - exportTableViewToCSVPerGene(_geneTableView); - - /*if (_selectedGene.getString() != "") - { - exportTableViewToCSVPerGene(_geneTableView); - } - else - { - exportTableViewToCSV(_geneTableView);_selectionDetailsTable - }*/ - - }; - connect(&_saveGeneTable, &TriggerAction::triggered, this, triggerSaveGeneTable); - - const auto triggerSaveSpeciesTable = [this]() -> void { - - - exportTableViewToCSVForGenes(_geneTableView); - - - }; - connect(&_saveSpeciesTable, &TriggerAction::triggered, this, triggerSaveSpeciesTable); - - - const auto updateComputeTreesToDisplayFromHierarchy = [this]() -> void { - - _computeTreesToDisplayFromHierarchy.setDisabled(true); - precomputeTreesFromHierarchy(); - _computeTreesToDisplayFromHierarchy.setDisabled(false); - - }; - - connect(&_computeTreesToDisplayFromHierarchy, &TriggerAction::triggered, this, updateComputeTreesToDisplayFromHierarchy); - - const auto updateGeneTableTSNECheck = [this]() -> void { - _statusColorAction.setString("M"); - - }; - connect(&_performGeneTableTsneAction, &ToggleAction::toggled, this, updateGeneTableTSNECheck); - const auto updateClusterCountSortingType = [this]() -> void { - updateClusterInfoStatusBar(); - - }; - connect(&_clusterCountSortingType, &OptionAction::currentIndexChanged, this, updateClusterCountSortingType); - QTimer* debounceTimer = new QTimer(this); - debounceTimer->setSingleShot(true); - debounceTimer->setInterval(500); // 500 milliseconds wait time - - const auto debouncelambda = [this]() -> void { // Capture debounceTimer by - _statusColorAction.setString("M"); - disableActions(); - - findTopNGenesPerCluster(); - if (_projectOpened) - { - _statusColorAction.setString("C"); - } - - enableActions(); - }; - - connect(debounceTimer, &QTimer::timeout, this, debouncelambda); - - const auto updateTopGenesSlider = [this, debounceTimer]() -> void { // Capture debounceTimer by reference - //wait to see if any more updates are coming then call findTopNGenesPerCluster(); - // Restart the timer every time the value changes - debounceTimer->start(); - }; - connect(&_topNGenesFilter, &IntegralAction::valueChanged, this, updateTopGenesSlider); - - - -} -/* -void SettingsAction::triggerTrippleHierarchyFrequencyChange() -{ - if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) - { - return; - } - _clusterSpeciesFrequencyMap.clear(); - auto startTimer = std::chrono::high_resolution_clock::now(); - qDebug() << "computeFrequencyMapForHierarchyItemsChange for all 3 levels Start"; - - if (!_speciesNamesDataset.getCurrentDataset().isValid() || !_mainPointsDataset.getCurrentDataset().isValid() || !_topClusterNamesDataset.getCurrentDataset().isValid() || !_middleClusterNamesDataset.getCurrentDataset().isValid() || !_bottomClusterNamesDataset.getCurrentDataset().isValid()) { - qDebug() << "Datasets are not valid"; - return; - } - - auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); - auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); - auto numOfPoints = mainPointDatasetFull->getNumPoints(); - std::vector topClusterNames(numOfPoints, true); - std::vector middleClusterNames(numOfPoints, true); - std::vector bottomClusterNames(numOfPoints, true); - QStringList topInclusionList; - QStringList middleInclusionList; - QStringList bottomInclusionList; - auto topClusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); - auto middleClusterDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); - auto bottomClusterDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); - - auto processTopClusters = [&]() { - if (topClusterDataset.isValid()) - { - for (const auto& cluster : topClusterDataset->getClusters()) - { - if (!topInclusionList.contains(cluster.getName())) - { - for (const auto& index : cluster.getIndices()) - { - topClusterNames[index] = false; - } - } - } - } - }; - - auto processMiddleClusters = [&]() { - if (middleClusterDataset.isValid()) - { - for (const auto& cluster : middleClusterDataset->getClusters()) - { - if (!middleInclusionList.contains(cluster.getName())) - { - for (const auto& index : cluster.getIndices()) - { - middleClusterNames[index] = false; - } - } - } - } - }; - - auto processBottomClusters = [&]() { - if (bottomClusterDataset.isValid()) - { - for (const auto& cluster : bottomClusterDataset->getClusters()) - { - if (!bottomInclusionList.contains(cluster.getName())) - { - for (const auto& index : cluster.getIndices()) - { - bottomClusterNames[index] = false; - } - } - } - } - }; - - // Run the three tasks in parallel - QFuture topFuture = QtConcurrent::run(processTopClusters); - QFuture middleFuture = QtConcurrent::run(processMiddleClusters); - QFuture bottomFuture = QtConcurrent::run(processBottomClusters); - - // Wait for all tasks to complete - topFuture.waitForFinished(); - middleFuture.waitForFinished(); - bottomFuture.waitForFinished(); - - if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) - { - auto speciesclusters = speciesClusterDatasetFull->getClusters(); - for (const auto& species : speciesclusters) { - auto speciesIndices = species.getIndices(); - auto speciesName = species.getName(); - int topCount = std::count_if(speciesIndices.begin(), speciesIndices.end(), [&topClusterNames](int index) { - return topClusterNames[index]; - }); - int middleCount = std::count_if(speciesIndices.begin(), speciesIndices.end(), [&middleClusterNames](int index) { - return middleClusterNames[index]; - }); - int bottomCount = std::count_if(speciesIndices.begin(), speciesIndices.end(), [&bottomClusterNames](int index) { - return bottomClusterNames[index]; - }); - - _clusterSpeciesFrequencyMap[speciesName]["topCells"] = topCount; - _clusterSpeciesFrequencyMap[speciesName]["middleCells"] = middleCount; - _clusterSpeciesFrequencyMap[speciesName]["bottomCells"] = bottomCount; - } - } - - auto endTimer = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(endTimer - startTimer).count(); - qDebug() << "Time taken for computeFrequencyMapForHierarchyItemsChange for all 3 levels: " + QString::number(duration / 1000.0) + " s"; -} -*/ -void SettingsAction::updateButtonTriggered() -{ - if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) - { - qDebug() << "Map for hierarchy items change method stop for project load blocker is checked"; - return; - } - - try { - // _startComputationTriggerAction.setDisabled(true); - startCodeTimer("UpdateGeneFilteringTrigger"); - //startCodeTimer("Part1"); - - int groupIDDeletion = 10; - int groupID1 = 10 * 2; - int groupID2 = 10 * 3; - clearTemporaryDatasetHandles(); - removeDatasets(groupIDDeletion); - auto pointsDataset = _mainPointsDataset.getCurrentDataset(); - auto embeddingDataset = _embeddingDataset.getCurrentDataset(); - auto speciesDataset = _speciesNamesDataset.getCurrentDataset(); - auto clusterDataset = _bottomClusterNamesDataset.getCurrentDataset(); - auto referenceTreeDataset = _referenceTreeDataset.getCurrentDataset(); - _selectedSpeciesVals.setString(""); - _geneNamesConnection.setString(""); - bool isValid = false; - - QString referenceTreedatasetId = ""; - //stopCodeTimer("Part1"); - //startCodeTimer("Part2"); - if (!pointsDataset.isValid() || !embeddingDataset.isValid() || !speciesDataset.isValid() || !clusterDataset.isValid() || !referenceTreeDataset.isValid()) - { - qDebug() << "No datasets selected"; - //_startComputationTriggerAction.setDisabled(false); - return; - } - if (pointsDataset->getSelectionIndices().size() < 1) - { - qDebug() << "No points selected"; - //_startComputationTriggerAction.setDisabled(false); - return; - } - /*if (_selectedPointsTSNEDataset.isValid()) - { - _selectedPointsTSNEDataset->setSelectionIndices({}); - }*/ - //stopCodeTimer("Part2"); - //startCodeTimer("Part3"); - _clusterNameToGeneNameToExpressionValue.clear(); - referenceTreedatasetId = referenceTreeDataset->getId(); - isValid = speciesDataset->getParent() == pointsDataset && clusterDataset->getParent() == pointsDataset && embeddingDataset->getParent() == pointsDataset; - if (!isValid) - { - qDebug() << "Datasets are not valid"; - //_startComputationTriggerAction.setDisabled(false); - return; - } - _selectedIndicesFromStorage.clear(); - _selectedIndicesFromStorage = pointsDataset->getSelectionIndices(); - - auto embeddingDatasetRaw = mv::data().getDataset(embeddingDataset->getId()); - auto pointsDatasetRaw = mv::data().getDataset(pointsDataset->getId()); - auto pointsDatasetallColumnNameList = pointsDatasetRaw->getDimensionNames(); - auto embeddingDatasetallColumnNameList = embeddingDatasetRaw->getDimensionNames(); - //stopCodeTimer("Part3"); - //startCodeTimer("Part4"); - std::vector embeddingDatasetColumnIndices(embeddingDatasetallColumnNameList.size()); - std::iota(embeddingDatasetColumnIndices.begin(), embeddingDatasetColumnIndices.end(), 0); - - std::vector pointsDatasetallColumnIndices(pointsDatasetallColumnNameList.size()); - std::iota(pointsDatasetallColumnIndices.begin(), pointsDatasetallColumnIndices.end(), 0); - //stopCodeTimer("Part4"); - { - - if (_selectedIndicesFromStorage.size() > 0 && embeddingDatasetColumnIndices.size() > 0) - { - //startCodeTimer("Part5"); - auto speciesDatasetRaw = mv::data().getDataset(speciesDataset->getId()); - auto clusterDatasetRaw = mv::data().getDataset(clusterDataset->getId()); - auto clusterDatasetName = clusterDatasetRaw->getGuiName(); - auto clustersValuesAll = clusterDatasetRaw->getClusters(); - auto speciesValuesAll = speciesDatasetRaw->getClusters(); - - std::map>> selectedClustersMap; - std::map>> selectedSpeciesMap; - //stopCodeTimer("Part5"); - if (!speciesValuesAll.empty() && !clustersValuesAll.empty()) - { - - //if (_selectedPointsTSNEDataset.isValid()) - //{ - //auto datasetIDLowRem = _selectedPointsTSNEDataset.getDatasetId(); - //mv::events().notifyDatasetAboutToBeRemoved(_selectedPointsTSNEDataset); - //mv::data().removeDataset(_selectedPointsTSNEDataset); - //mv::events().notifyDatasetRemoved(datasetIDLowRem, PointType); - //} - - // _selectedPointsDataset = Dataset(); - //_selectedPointsEmbeddingDataset = Dataset(); - //startCodeTimer("Part6.1"); - /*if (!_selectedPointsDataset.isValid()) - { - _selectedPointsDataset = mv::data().createDataset("Points", "SelectedPointsDataset"); - _selectedPointsDataset->setGroupIndex(10); - mv::events().notifyDatasetAdded(_selectedPointsDataset); - - }*/ - - pointsDatasetRaw->setSelectionIndices(_selectedIndicesFromStorage); - _selectedPointsDataset = pointsDatasetRaw->createSubsetFromSelection("SelectedPointsDataset"); - _selectedPointsDataset->setGroupIndex(groupIDDeletion); - - if (!_tsneDatasetExpressionColors.isValid()) - { - _tsneDatasetExpressionColors = mv::data().createDataset("Points", "TSNEDatasetExpressionColors", _selectedPointsDataset); - _tsneDatasetExpressionColors->setGroupIndex(groupIDDeletion); - mv::events().notifyDatasetAdded(_tsneDatasetExpressionColors); - - } - - embeddingDatasetRaw->setSelectionIndices(_selectedIndicesFromStorage); - _selectedPointsEmbeddingDataset = embeddingDatasetRaw->createSubsetFromSelection("TSNEDataset", _selectedPointsDataset); - _selectedPointsEmbeddingDataset->setGroupIndex(groupIDDeletion); - - - if (!_tsneDatasetSpeciesColors.isValid()) - { - _tsneDatasetSpeciesColors = mv::data().createDataset("Cluster", "TSNEDatasetSpeciesColors", _selectedPointsDataset); - _tsneDatasetSpeciesColors->setGroupIndex(groupIDDeletion); - mv::events().notifyDatasetAdded(_tsneDatasetSpeciesColors); - } - - if (!_tsneDatasetClusterColors.isValid()) - { - _tsneDatasetClusterColors = mv::data().createDataset("Cluster", "TSNEDatasetClusterColors", _selectedPointsDataset); - _tsneDatasetClusterColors->setGroupIndex(groupIDDeletion); - mv::events().notifyDatasetAdded(_tsneDatasetClusterColors); - } - - - if (!_filteredUMAPDatasetPoints.isValid()) - { - _filteredUMAPDatasetPoints = mv::data().createDataset("Points", "Filtered UMAP Dataset Points"); - _filteredUMAPDatasetPoints->setGroupIndex(groupID1); - mv::events().notifyDatasetAdded(_filteredUMAPDatasetPoints); - if (!_filteredUMAPDatasetColors.isValid()) - { - //need to delete - - } - if (!_filteredUMAPDatasetClusters.isValid()) - { - //need to delete - - } - _filteredUMAPDatasetColors = mv::data().createDataset("Points", "Filtered UMAP Dataset Colors", _filteredUMAPDatasetPoints); - _filteredUMAPDatasetColors->setGroupIndex(groupID1); - mv::events().notifyDatasetAdded(_filteredUMAPDatasetColors); - - _filteredUMAPDatasetClusters = mv::data().createDataset("Cluster", "Filtered UMAP Dataset Clusters", _filteredUMAPDatasetPoints); - _filteredUMAPDatasetClusters->setGroupIndex(groupID1); - mv::events().notifyDatasetAdded(_filteredUMAPDatasetClusters); - - } - - - - /*if (!_selectedPointsEmbeddingDataset.isValid()) - { - _selectedPointsEmbeddingDataset = mv::data().createDataset("Points", "TSNEDataset", _selectedPointsDataset); - _selectedPointsEmbeddingDataset->setGroupIndex(10); - mv::events().notifyDatasetAdded(_selectedPointsEmbeddingDataset); - - }*/ - - - if (!_geneSimilarityPoints.isValid()) - { - _geneSimilarityPoints = mv::data().createDataset("Points", "GeneSimilarityPoints"); - _geneSimilarityPoints->setGroupIndex(groupID2); - mv::events().notifyDatasetAdded(_geneSimilarityPoints); - } - if (!_geneSimilarityClusterColoring.isValid()) - { - _geneSimilarityClusterColoring = mv::data().createDataset("Cluster", "GeneSimilarityClusterColoring", _geneSimilarityPoints); - _geneSimilarityClusterColoring->setGroupIndex(groupID2); - mv::events().notifyDatasetAdded(_geneSimilarityClusterColoring); - - } - //_geneSimilarityClusters.clear(); - //stopCodeTimer("Part6.1"); - if (_selectedPointsDataset.isValid() && _selectedPointsEmbeddingDataset.isValid() && _tsneDatasetSpeciesColors.isValid() && _tsneDatasetClusterColors.isValid() && _geneSimilarityPoints.isValid() && _geneSimilarityClusterColoring.isValid()) - { - //startCodeTimer("Part6.2"); - //_tsneDatasetSpeciesColors->getClusters() = QVector(); - //events().notifyDatasetDataChanged(_tsneDatasetSpeciesColors); - //_tsneDatasetClusterColors->getClusters() = QVector(); - //events().notifyDatasetDataChanged(_tsneDatasetClusterColors); - _geneSimilarityClusterColoring->getClusters() = QVector(); - events().notifyDatasetDataChanged(_geneSimilarityClusterColoring); - //stopCodeTimer("Part6.2"); - //startCodeTimer("Part7"); - //startCodeTimer("Part7.1"); - int selectedIndicesFromStorageSize = static_cast(_selectedIndicesFromStorage.size()); - int pointsDatasetColumnsSize = static_cast(pointsDatasetallColumnIndices.size()); - int embeddingDatasetColumnsSize = static_cast(embeddingDatasetColumnIndices.size()); - //QString datasetIdEmb = _selectedPointsDataset->getId(); - //QString datasetId = _selectedPointsEmbeddingDataset->getId(); - int dimofDatasetExp = 1; - std::vector dimensionNamesExp = { "Expression" }; - QString datasetIdExp = _tsneDatasetExpressionColors->getId(); - //stopCodeTimer("Part7.1"); - //startCodeTimer("Part7.2"); - - // Define result containers outside the lambda functions to ensure they are accessible later - //std::vector resultContainerForSelectedPoints(selectedIndicesFromStorageSize * pointsDatasetColumnsSize); - //std::vector resultContainerForSelectedEmbeddingPoints(selectedIndicesFromStorageSize * embeddingDatasetColumnsSize); - std::vector resultContainerColorPoints(selectedIndicesFromStorageSize, -1.0f); - - //first thread start - //auto future1 = std::async(std::launch::async, [&]() { - //pointsDatasetRaw->populateDataForDimensions(resultContainerForSelectedPoints, pointsDatasetallColumnIndices, _selectedIndicesFromStorage); - // }); - - //second thread start - // auto future2 = std::async(std::launch::async, [&]() { - //embeddingDatasetRaw->populateDataForDimensions(resultContainerForSelectedEmbeddingPoints, embeddingDatasetColumnIndices, _selectedIndicesFromStorage); - // }); - - - // Wait for all futures to complete before proceeding - //future1.wait(); - //future2.wait(); - - - //startCodeTimer("Part7.2.1"); - //needs to wait for future1 finish only - //populatePointData(datasetIdEmb, resultContainerForSelectedPoints, selectedIndicesFromStorageSize, pointsDatasetColumnsSize, pointsDatasetallColumnNameList); - //stopCodeTimer("Part7.2.1"); - - //startCodeTimer("Part7.2.2"); - //needs to wait for future2 finish only - //populatePointData(datasetId, resultContainerForSelectedEmbeddingPoints, selectedIndicesFromStorageSize, embeddingDatasetColumnsSize, embeddingDatasetallColumnNameList); - //stopCodeTimer("Part7.2.2"); - - //startCodeTimer("Part7.2.3"); - //needs to wait for future3 finish only - populatePointData(datasetIdExp, resultContainerColorPoints, selectedIndicesFromStorageSize, dimofDatasetExp, dimensionNamesExp); - //stopCodeTimer("Part7.2.3"); - - //stopCodeTimer("Part7.2"); - - - //stopCodeTimer("Part7"); - //startCodeTimer("Part8"); - if (_selectedPointsTSNEDataset.isValid()) - { - auto runningAction = dynamic_cast(_selectedPointsTSNEDataset->findChildByPath("TSNE/TsneComputationAction/Running")); - - if (runningAction) - { - - if (runningAction->isChecked()) - { - auto stopAction = dynamic_cast(_selectedPointsTSNEDataset->findChildByPath("TSNE/TsneComputationAction/Stop")); - if (stopAction) - { - stopAction->trigger(); - std::this_thread::sleep_for(std::chrono::seconds(5)); - } - } - - } - } - //stopCodeTimer("Part8"); - //startCodeTimer("Part9"); - if (!_performGeneTableTsneAction.isChecked()) - { - - - mv::plugin::AnalysisPlugin* analysisPlugin; - bool usePreTSNE = _usePreComputedTSNE.isChecked(); - - auto scatterplotModificationsLowDimUMAP = [this]() { - if (_selectedPointsTSNEDataset.isValid()) { - auto scatterplotViewFactory = mv::plugins().getPluginFactory("Scatterplot View"); - mv::gui::DatasetPickerAction* colorDatasetPickerAction; - mv::gui::DatasetPickerAction* pointDatasetPickerAction; - mv::gui::ViewPluginSamplerAction* samplerActionAction; - if (scatterplotViewFactory) { - for (auto plugin : mv::plugins().getPluginsByFactory(scatterplotViewFactory)) { - if (plugin->getGuiName() == "Scatterplot Cell Selection Overview") { - pointDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Position")); - if (pointDatasetPickerAction) { - pointDatasetPickerAction->setCurrentText(""); - - pointDatasetPickerAction->setCurrentDataset(_selectedPointsTSNEDataset); - - colorDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Color")); - if (colorDatasetPickerAction) - { - colorDatasetPickerAction->setCurrentText(""); - - - - auto selectedColorType = _scatterplotReembedColorOption.getCurrentText(); - if (selectedColorType != "") - { - if (selectedColorType == "Cluster") - { - if (_bottomClusterNamesDataset.getCurrentDataset().isValid()) - { - colorDatasetPickerAction->setCurrentDataset(_bottomClusterNamesDataset.getCurrentDataset()); - - auto legendViewFactory = mv::plugins().getPluginFactory("ChartLegend View"); - if (legendViewFactory) - { - for (auto legendPlugin : mv::plugins().getPluginsByFactory(legendViewFactory)) - { - if (legendPlugin->getGuiName() == "Legend View") - { - //legendPlugin->printChildren(); - auto legendDatasetPickerAction = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster dataset")); - if (legendDatasetPickerAction) - { - legendDatasetPickerAction->setCurrentDataset(_bottomClusterNamesDataset.getCurrentDataset()); - } - auto chartTitle = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Chart Title")); - if (chartTitle) - { - chartTitle->setString("Cell types"); - } - /* - auto selectionColor = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Selection color")); - if (selectionColor) - { - selectionColor->setColor(QColor(53, 126, 199)); - } - auto selectionStringDelimiter = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Delimiter")); - if (selectionStringDelimiter) - { - selectionStringDelimiter->setString(","); - } - auto selectionClustersString = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster Selection string")); - if (selectionClustersString) - { - selectionClustersString->setString(""); //TODO - } - */ - } - } - } - - - } - } - else if (selectedColorType == "Species") - { - if (_speciesNamesDataset.getCurrentDataset().isValid()) - { - colorDatasetPickerAction->setCurrentDataset(_speciesNamesDataset.getCurrentDataset()); - } - } - else if (selectedColorType == "Expression") - { - if (_tsneDatasetExpressionColors.isValid()) - { - colorDatasetPickerAction->setCurrentDataset(_tsneDatasetExpressionColors); - } - } - - - - } - } - - samplerActionAction = plugin->findChildByPath("Sampler"); - - if (samplerActionAction) - { - samplerActionAction->setHtmlViewGeneratorFunction([this](const ViewPluginSamplerAction::SampleContext& toolTipContext) -> QString { - QString clusterDatasetId = _speciesNamesDataset.getCurrentDataset().getDatasetId(); - return generateTooltip(toolTipContext, clusterDatasetId, true, "GlobalPointIndices"); - }); - } - } - } - } - } - } - - }; - //stopCodeTimer("Part9"); - if (!usePreTSNE) - { - //startCodeTimer("Part10"); - analysisPlugin = mv::plugins().requestPlugin("tSNE Analysis", { _selectedPointsEmbeddingDataset }); - if (!analysisPlugin) { - qDebug() << "Could not find create TSNE Analysis"; - return; - } - _selectedPointsTSNEDataset = analysisPlugin->getOutputDataset(); - _selectedPointsTSNEDataset->setGroupIndex(groupIDDeletion); - if (_selectedPointsTSNEDataset.isValid()) - { - - int perplexity = std::min(static_cast(_selectedIndicesFromStorage.size()), _tsnePerplexity.getValue()); - if (perplexity < 5) - { - qDebug() << "Perplexity is less than 5"; - //_startComputationTriggerAction.setDisabled(false); - return; - } - if (perplexity != _tsnePerplexity.getValue()) - { - _tsnePerplexity.setValue(perplexity); - } - - auto perplexityAction = dynamic_cast(_selectedPointsTSNEDataset->findChildByPath("TSNE/Perplexity")); - if (perplexityAction) - { - qDebug() << "Perplexity: Found"; - perplexityAction->setValue(perplexity); - } - else - { - qDebug() << "Perplexity: Not Found"; - } - - scatterplotModificationsLowDimUMAP(); - - auto startAction = dynamic_cast(_selectedPointsTSNEDataset->findChildByPath("TSNE/TsneComputationAction/Start")); - if (startAction) { - - startAction->trigger(); - - analysisPlugin->getOutputDataset()->setSelectionIndices({}); - } - - } - //stopCodeTimer("Part10"); - } - else - { - //startCodeTimer("Part11"); - auto umapDataset = _scatterplotEmbeddingPointsUMAPOption.getCurrentDataset(); - - if (umapDataset.isValid()) - { - - - _selectedPointsTSNEDataset = mv::data().createDerivedDataset("SelectedPointsTSNEDataset", _selectedPointsEmbeddingDataset, _selectedPointsEmbeddingDataset); - _selectedPointsTSNEDataset->setGroupIndex(groupIDDeletion); - mv::events().notifyDatasetAdded(_selectedPointsTSNEDataset); - - auto umapDatasetRaw = mv::data().getDataset(umapDataset->getId()); - auto dimNames = umapDatasetRaw->getDimensionNames(); - int preComputedEmbeddingColumnsSize = umapDatasetRaw->getNumDimensions(); - std::vector resultContainerPreComputedUMAP(selectedIndicesFromStorageSize * preComputedEmbeddingColumnsSize); - std::vector preComputedEmbeddingColumnIndices(preComputedEmbeddingColumnsSize); - - std::iota(preComputedEmbeddingColumnIndices.begin(), preComputedEmbeddingColumnIndices.end(), 0); - - umapDatasetRaw->populateDataForDimensions(resultContainerPreComputedUMAP, preComputedEmbeddingColumnIndices, _selectedIndicesFromStorage); - - QString datasetId = _selectedPointsTSNEDataset->getId(); - populatePointData(datasetId, resultContainerPreComputedUMAP, selectedIndicesFromStorageSize, preComputedEmbeddingColumnsSize, dimNames); - - if (_selectedPointsTSNEDataset.isValid()) - { - scatterplotModificationsLowDimUMAP(); - } - } - else - { - qDebug() << "UMAP Dataset not valid"; - } - - - //stopCodeTimer("Part11"); - - - - } - } - - - } - else - { - qDebug() << "Datasets are not valid"; - } - //startCodeTimer("Part12"); - //startCodeTimer("Part12.1"); - QFuture futureClusterCVals = QtConcurrent::run([&]() { - QMutex mutex; - for (auto& clusters : clustersValuesAll) { - auto clusterIndices = clusters.getIndices(); - auto clusterName = clusters.getName(); - auto clusterColor = clusters.getColor(); - std::vector filteredIndices; - - QtConcurrent::blockingMap(clusterIndices, [&](int index) { - int indexVal = findIndex(_selectedIndicesFromStorage, index); - if (indexVal != -1) { - QMutexLocker locker(&mutex); - filteredIndices.push_back(indexVal); - } - }); - - { - QMutexLocker locker(&mutex); - selectedClustersMap[clusterName] = { clusterColor, filteredIndices }; - } - } - }); - QFuture futureSpeciesCVals = QtConcurrent::run([&]() { - QMutex mutex; - for (auto& clusters : speciesValuesAll) { - auto clusterIndices = clusters.getIndices(); - auto clusterName = clusters.getName(); - auto clusterColor = clusters.getColor(); - std::vector filteredIndices; - - QtConcurrent::blockingMap(clusterIndices, [&](int index) { - int indexVal = findIndex(_selectedIndicesFromStorage, index); - if (indexVal != -1) { - QMutexLocker locker(&mutex); - filteredIndices.push_back(indexVal); - } - }); - - { - QMutexLocker locker(&mutex); - selectedSpeciesMap[clusterName] = { clusterColor, filteredIndices }; - } - } - }); - futureClusterCVals.waitForFinished(); // Wait for the concurrent task to complete - futureSpeciesCVals.waitForFinished(); - //stopCodeTimer("Part12.1"); - - - - //startCodeTimer("Part12.2"); - std::sort(_selectedIndicesFromStorage.begin(), _selectedIndicesFromStorage.end()); - //_currentHierarchyItemsTopForTable.clear(); - //_currentHierarchyItemsMiddleForTable.clear(); - QStringList inclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - _currentHierarchyItemsMiddleForTable = QStringList{}; - if (_topSelectedHierarchyStatus.getString() != "") - { - - QStringList list = _topSelectedHierarchyStatus.getString().split(" @%$,$%@ "); - for (const auto& item : list) { - - if (inclusionList.contains(item)) - { - _currentHierarchyItemsMiddleForTable.push_back(item); - } - - } - } - else - { - _currentHierarchyItemsMiddleForTable = QStringList{}; - } - - - //_currentHierarchyItemsMiddleForTable = QSet{}; - // - /*for (const auto& [key, clusterIndicesMap] : _topHierarchyClusterMap) - { - - for (const auto& index : _selectedIndicesFromStorage) - { - if (clusterIndicesMap.at(index)) - { - - if (inclusionList.contains(key)) - { - _currentHierarchyItemsMiddleForTable.insert(key); - } - break; - } - } - }*/ - //qDebug() << "Middle Hierarchy Items: " << _currentHierarchyItemsMiddleForTable; - - // - - - QMutex mutex; // Mutex for thread safety - - QtConcurrent::blockingMap(speciesValuesAll, [&](auto& species) { - auto speciesIndices = species.getIndices(); - auto speciesName = species.getName(); - auto speciesColor = species.getColor(); - - std::vector commonSelectedIndices; - - std::sort(speciesIndices.begin(), speciesIndices.end()); - std::set_intersection(_selectedIndicesFromStorage.begin(), _selectedIndicesFromStorage.end(), speciesIndices.begin(), speciesIndices.end(), std::back_inserter(commonSelectedIndices)); - std::unordered_map localClusterNameToGeneNameToExpressionValue; - - - - int allTopCounts = 0; - int selectedInclusionCounts = 0; - int allMiddleCounts = 0; - int allCellCount = 0; - int selectedCellCount = commonSelectedIndices.size(); - - { - QMutexLocker locker(&mutex); - auto it = _clusterGeneMeanExpressionMap[speciesName].begin(); - if (it != _clusterGeneMeanExpressionMap[speciesName].end()) { - allCellCount = it->second.first; - } - } - int nonSelectedCellsCount = allCellCount - selectedCellCount; - - for (const auto& cluster : _topHierarchyClusterMap) { - if (inclusionList.contains(cluster.first)) { - bool clusterPresent = false; - auto currentInclusionClusterMap = cluster.second; - { - //QMutexLocker locker(&mutex); - //_currentHierarchyItemsTopForTable.insert(cluster.first); - } - int clusterSize = 0; - - for (auto speciesIndex : speciesIndices) { - if (currentInclusionClusterMap[speciesIndex]) { - clusterSize++; - } - } - - for (auto ind : commonSelectedIndices) { - if (currentInclusionClusterMap[ind]) { - selectedInclusionCounts++; - clusterPresent = true; - } - } - allTopCounts += clusterSize; - - if (clusterPresent) { - allMiddleCounts += clusterSize; - { - //QMutexLocker locker(&mutex); - //_currentHierarchyItemsMiddleForTable.insert(cluster.first); - } - } - } - } - - for (int i = 0; i < pointsDatasetallColumnNameList.size(); i++) { - const auto& geneName = pointsDatasetallColumnNameList[i]; - std::vector geneIndex = { i }; - - float allCellMean = 0.0f; - { - QMutexLocker locker(&mutex); - allCellMean = _clusterGeneMeanExpressionMap[speciesName][geneName].second; - } - - float nonSelectedMean = 0.0; - float selectedCellsMean = 0.0; - if (!commonSelectedIndices.empty()) { - std::vector resultContainerShort(commonSelectedIndices.size()); - pointsDatasetRaw->populateDataForDimensions(resultContainerShort, geneIndex, commonSelectedIndices); - - float allCellTotal = allCellMean * allCellCount; - selectedCellsMean = calculateMean(resultContainerShort); - - if (nonSelectedCellsCount > 0) { - nonSelectedMean = (allCellTotal - (selectedCellsMean * selectedCellCount)) / nonSelectedCellsCount; - } - } else { - nonSelectedMean = allCellMean; - } - - Stats valueStats; - valueStats.abundanceMiddle = allMiddleCounts; - valueStats.abundanceTop = allTopCounts; - valueStats.countSelected = selectedCellCount; - valueStats.countNonSelected = nonSelectedCellsCount; - valueStats.meanSelected = selectedCellsMean; - valueStats.meanNonSelected = nonSelectedMean; - valueStats.color = speciesColor; - valueStats.countAbundanceNumerator = selectedInclusionCounts; - - localClusterNameToGeneNameToExpressionValue[geneName] = valueStats; - } - - QMutexLocker locker(&mutex); - for (const auto& pair : localClusterNameToGeneNameToExpressionValue) { - _clusterNameToGeneNameToExpressionValue[speciesName][pair.first] = pair.second; - _selectedSpeciesCellCountMap[speciesName].selectedCellsCount = pair.second.countSelected; - _selectedSpeciesCellCountMap[speciesName].nonSelectedCellsCount = pair.second.countNonSelected; - _selectedSpeciesCellCountMap[speciesName].abundanceMiddle = pair.second.abundanceMiddle; - _selectedSpeciesCellCountMap[speciesName].abundanceTop = pair.second.abundanceTop; - _selectedSpeciesCellCountMap[speciesName].countAbundanceNumerator = pair.second.countAbundanceNumerator; - } - }); - - - - - //stopCodeTimer("Part12.2"); - - auto clusterColorDatasetId = _tsneDatasetClusterColors->getId(); - auto speciesColorDatasetId = _tsneDatasetSpeciesColors->getId(); - //startCodeTimer("Part12.3"); - populateClusterData(speciesColorDatasetId, selectedSpeciesMap); - //stopCodeTimer("Part12.3"); - //startCodeTimer("Part12.4"); - populateClusterData(clusterColorDatasetId, selectedClustersMap); - //stopCodeTimer("Part12.4"); - //stopCodeTimer("Part12"); - updateClusterInfoStatusBar(); - /* - QLayoutItem* layoutItem; - while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { - delete layoutItem->widget(); - delete layoutItem; - } - if (_tsneDatasetClusterColors.isValid()) - { - - auto clusterValues = _tsneDatasetClusterColors->getClusters(); - if (!clusterValues.empty()) - { - //startCodeTimer("Part13"); - - //QLayoutItem* layoutItem; - //while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { - // delete layoutItem->widget(); - // delete layoutItem; - //} - - // Create a description label - auto descriptionLabel = new QLabel("Selected Cell Counts per " + clusterDatasetName + " :"); - // Optionally, set a stylesheet for the description label for styling - descriptionLabel->setStyleSheet("QLabel { font-weight: bold; padding: 2px; }"); - // Add the description label to the layout - _selectedCellClusterInfoStatusBar->addWidget(descriptionLabel); - - - std::vector orderedClustersSet; - - for (const auto& cluster : clusterValues) { - ClusterOrderContainer temp{ - cluster.getIndices().size(), - cluster.getColor(), - cluster.getName() - }; - orderedClustersSet.push_back(std::move(temp)); - } - - const auto& currentText = _clusterCountSortingType.getCurrentText(); - if (currentText == "Name") { - std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), sortByName); - } - else if (currentText == "Hierarchy View" && !_customOrderClustersFromHierarchy.empty()) { - if (_customOrderClustersFromHierarchyMap.empty()) { - _customOrderClustersFromHierarchyMap = prepareCustomSortMap(_customOrderClustersFromHierarchy); - } - std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), [&](const ClusterOrderContainer& a, const ClusterOrderContainer& b) { - return sortByCustomList(a, b, _customOrderClustersFromHierarchyMap); - }); - } - else { - std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), sortByCount); - if (currentText != "Count") { - _clusterCountSortingType.setCurrentText("Count"); - } - } - - for (const auto& clustersFromSet : orderedClustersSet) - { - auto clusterLabel = new QLabel(QString("%1: %2").arg(clustersFromSet.name).arg(clustersFromSet.count)); - QColor textColor = clustersFromSet.color.lightness() > 127 ? Qt::black : Qt::white; - clusterLabel->setStyleSheet(QString("QLabel { color: %1; background-color: %2; padding: 2px; border: 0.5px solid %3; }") - .arg(textColor.name()).arg(clustersFromSet.color.name(QColor::HexArgb)).arg(textColor.name())); - _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); - } - - - - - - //for (auto cluster : clusterValues) { - // auto clusterName = cluster.getName(); - // auto clusterIndicesSize = cluster.getIndices().size(); - // auto clusterColor = cluster.getColor(); // Assuming getColor() returns a QColor - - // // Calculate luminance - // qreal luminance = 0.299 * clusterColor.redF() + 0.587 * clusterColor.greenF() + 0.114 * clusterColor.blueF(); - - // // Choose text color based on luminance - // QString textColor = (luminance > 0.5) ? "black" : "white"; - - // // Convert QColor to hex string for stylesheet - // QString backgroundColor = clusterColor.name(QColor::HexArgb); - - // auto clusterLabel = new QLabel(QString("%1: %2").arg(clusterName).arg(clusterIndicesSize)); - // // Add text color and background color to clusterLabel with padding and border for better styling - // clusterLabel->setStyleSheet(QString("QLabel { color: %1; background-color: %2; padding: 2px; border: 0.5px solid %3; }") - // .arg(textColor).arg(backgroundColor).arg(textColor)); - // _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); - //} - - - } - - } - */ - //the next line should only execute if all above are finished - - - //startCodeTimer("Part14"); - findTopNGenesPerCluster(); - //stopCodeTimer("Part14"); - - - - - } - - else - { - qDebug() << "Species or Clusters are empty"; - } - - - } - - else - { - qDebug() << "No points selected or no dimensions present"; - } - - _removeRowSelection.trigger(); - _removeRowSelection.setEnabled(false); - //enableDisableButtonsAutomatically(); - - } - stopCodeTimer("UpdateGeneFilteringTrigger"); - //_startComputationTriggerAction.setDisabled(false); - } - catch (const std::exception& e) { - qDebug() << "An exception occurred in coputation: " << e.what(); - _statusColorAction.setString("E"); - } - catch (...) { - qDebug() << "An unknown exception occurred in coputation"; - _statusColorAction.setString("E"); - } -} - -void SettingsAction::updateClusterInfoStatusBar() -{ - QLayoutItem* layoutItem; - while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { - delete layoutItem->widget(); - delete layoutItem; - } - if (_tsneDatasetClusterColors.isValid() && _bottomClusterNamesDataset.getCurrentDataset().isValid()) - { - auto clusterDatasetName = _bottomClusterNamesDataset.getCurrentDataset()->getGuiName(); - auto clusterValues = _tsneDatasetClusterColors->getClusters(); - if (!clusterValues.empty()) - { - //startCodeTimer("Part13"); - - /*QLayoutItem* layoutItem; - while ((layoutItem = _selectedCellClusterInfoStatusBar->takeAt(0)) != nullptr) { - delete layoutItem->widget(); - delete layoutItem; - }*/ - - // Create a description label - auto descriptionLabel = new QLabel("Cell counts per " + clusterDatasetName + ", sorted by " + _clusterCountSortingType.getCurrentText() + ":"); - - // Optionally, set a stylesheet for the description label for styling - descriptionLabel->setStyleSheet("QLabel { font-weight: bold; padding: 2px; }"); - // Add the description label to the layout - _selectedCellClusterInfoStatusBar->addWidget(descriptionLabel); - - - std::vector orderedClustersSet; - - for (const auto& cluster : clusterValues) { - ClusterOrderContainer temp{ - static_cast(cluster.getIndices().size()), - cluster.getColor(), - cluster.getName() - }; - orderedClustersSet.push_back(std::move(temp)); - } - - const auto& currentText = _clusterCountSortingType.getCurrentText(); - if (currentText == "Name") { - std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), sortByName); - } - else if (currentText == "Hierarchy View" && !_customOrderClustersFromHierarchy.empty()) { - if (_customOrderClustersFromHierarchyMap.empty()) { - _customOrderClustersFromHierarchyMap = prepareCustomSortMap(_customOrderClustersFromHierarchy); - } - std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), [&](const ClusterOrderContainer& a, const ClusterOrderContainer& b) { - return sortByCustomList(a, b, _customOrderClustersFromHierarchyMap); - }); - } - else { - std::sort(orderedClustersSet.begin(), orderedClustersSet.end(), sortByCount); - if (currentText != "Count") { - _clusterCountSortingType.setCurrentText("Count"); - } - } - QString selectedClustersString = ""; - for (const auto& clustersFromSet : orderedClustersSet) - { - auto clusterLabel = new ClickableLabel(); // Create the label without text - QString labelText = QString("%1: %2").arg(clustersFromSet.name).arg(clustersFromSet.count); - clusterLabel->setText(labelText); // Set the text on the label - selectedClustersString = selectedClustersString + clustersFromSet.name + ","; - QColor textColor = clustersFromSet.color.lightness() > 127 ? Qt::black : Qt::white; - clusterLabel->setStyleSheet(QString("ClickableLabel { color: %1; background-color: %2; padding: 2px; border: 0.5px solid %3; }") - .arg(textColor.name()).arg(clustersFromSet.color.name(QColor::HexArgb)).arg(textColor.name())); - connect(clusterLabel, &ClickableLabel::clicked, this, [this, clusterLabel]() { - - - int current = _clusterCountSortingType.getCurrentIndex(); - int newIndex; - if (current == 0) - { - newIndex = 1; - } - else if (current == 1) - { - - if (!_customOrderClustersFromHierarchy.empty()) - { - newIndex = 2; - } - else - { - newIndex = 0; - } - } - else - { - newIndex = 0; - } - _clusterCountSortingType.setCurrentIndex(newIndex); - }); - - _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); - } - - auto legendViewFactory = mv::plugins().getPluginFactory("ChartLegend View"); - if (legendViewFactory) - { - for (auto legendPlugin : mv::plugins().getPluginsByFactory(legendViewFactory)) - { - if (legendPlugin->getGuiName() == "Legend View") - { - auto selectionColor = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Selection color")); - if (selectionColor) - { - selectionColor->setColor(QColor(53, 126, 199)); - } - auto selectionStringDelimiter = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Delimiter")); - if (selectionStringDelimiter) - { - selectionStringDelimiter->setString(","); - } - auto selectionClustersString = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster Selection string")); - if (selectionClustersString) - { - selectionClustersString->setString(selectedClustersString); - } - } - } - } - - /* - - for (auto cluster : clusterValues) { - auto clusterName = cluster.getName(); - auto clusterIndicesSize = cluster.getIndices().size(); - auto clusterColor = cluster.getColor(); // Assuming getColor() returns a QColor - - // Calculate luminance - qreal luminance = 0.299 * clusterColor.redF() + 0.587 * clusterColor.greenF() + 0.114 * clusterColor.blueF(); - - // Choose text color based on luminance - QString textColor = (luminance > 0.5) ? "black" : "white"; - - // Convert QColor to hex string for stylesheet - QString backgroundColor = clusterColor.name(QColor::HexArgb); - - auto clusterLabel = new QLabel(QString("%1: %2").arg(clusterName).arg(clusterIndicesSize)); - // Add text color and background color to clusterLabel with padding and border for better styling - clusterLabel->setStyleSheet(QString("QLabel { color: %1; background-color: %2; padding: 2px; border: 0.5px solid %3; }") - .arg(textColor).arg(backgroundColor).arg(textColor)); - _selectedCellClusterInfoStatusBar->addWidget(clusterLabel); - } - */ - - } - - } -} - - -void SettingsAction::setModifiedTriggeredData(QVariant geneListTable) -{ - if (!geneListTable.isNull()) - { - ////startCodeTimer("Part15"); - //_filteredGeneNamesVariant.setVariant(geneListTable); - _listModel.setVariant(geneListTable); - ////stopCodeTimer("Part15"); - - } - else - { - qDebug() << "QVariant empty"; - } -} - -void createTreeInitial(QJsonObject& node, const std::map& utilityMap) { - // Check if the "name" key exists in the current node - if (node.contains("name")) { - QString nodeName = node["name"].toString(); - auto it = utilityMap.find(nodeName); - - if (it != utilityMap.end()) { - node["mean"] = std::round(it->second.meanVal * 100.0) / 100.0; // Round to 2 decimal places - node["differential"] = std::round(it->second.differentialVal * 100.0) / 100.0; // Round to 2 decimal places - - - float topAbundance = 0.0; - if (it->second.abundanceTop != 0) - { - - topAbundance = (static_cast(it->second.countAbundanceNumerator) / static_cast(it->second.abundanceTop)) * 100; - } - - float middleAbundance = 0.0; - if (it->second.abundanceMiddle != 0) - { - - middleAbundance = (static_cast(it->second.countAbundanceNumerator) / static_cast(it->second.abundanceMiddle)) * 100; - } - - - node["abundanceTop"] = topAbundance; - node["abundanceMiddle"] = middleAbundance; - node["rank"] = it->second.rankVal; - node["gene"] = it->second.geneName; - } - } - - // If the node has "children", recursively update them as well - if (node.contains("children")) { - QJsonArray children = node["children"].toArray(); - for (int i = 0; i < children.size(); ++i) { - QJsonObject child = children[i].toObject(); - createTreeInitial(child, utilityMap); // Recursive call - children[i] = child; // Update the modified object back into the array - } - node["children"] = children; // Update the modified array back into the parent JSON object - } -} - - -void SettingsAction::precomputeTreesFromHierarchy() -{ - if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) - { - return; - } - _precomputedTreesFromTheHierarchy.clear(); - auto start = std::chrono::high_resolution_clock::now(); - qDebug() << "Computing precomputeTreesFromHierarchy"; - - if (!_speciesNamesDataset.getCurrentDataset().isValid() || !_mainPointsDataset.getCurrentDataset().isValid() || !_topClusterNamesDataset.getCurrentDataset().isValid() || !_middleClusterNamesDataset.getCurrentDataset().isValid() || !_bottomClusterNamesDataset.getCurrentDataset().isValid() || !_referenceTreeDataset.getCurrentDataset().isValid()) { - qDebug() << "Datasets are not valid"; - return; - } - auto speciesNamesDataset = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); - auto mainPointsDataset = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); - auto topClusterNamesDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); - auto middleClusterNamesDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); - auto bottomClusterNamesDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); - - auto referenceTreeDataset = mv::data().getDataset(_referenceTreeDataset.getCurrentDataset().getDatasetId()); - QJsonObject speciesDataJson = referenceTreeDataset->getTreeData(); - QStringList speciesNamesVerify = referenceTreeDataset->getTreeLeafNames(); - if (speciesDataJson.isEmpty() || speciesNamesVerify.isEmpty()) - { - qDebug() << "Reference tree data is empty"; - return; - } - - - if (speciesNamesDataset.isValid() && mainPointsDataset.isValid() && topClusterNamesDataset.isValid() && middleClusterNamesDataset.isValid() && bottomClusterNamesDataset.isValid()) - { - auto speciesClusters = speciesNamesDataset->getClusters(); - - //check if speciesNamesVerify and speciesClusters contain the same strings maybe in different order but same number and same value - if (speciesNamesVerify.size() != speciesClusters.size()) - { - qDebug() << "Species names do not match"; - return; - } - for (int i = 0; i < speciesNamesVerify.size(); i++) - { - if (speciesNamesVerify[i] != speciesClusters[i].getName()) - { - qDebug() << "Species names do not match"; - return; - } - } - - - auto mainPointDimensionNames = mainPointsDataset->getDimensionNames(); - auto mainPointsNumOfIndices = mainPointsDataset->getNumPoints(); - auto mainPointsNumOfDims = mainPointsDataset->getNumDimensions(); - - QVector topClusters = topClusterNamesDataset->getClusters(); - QVector middleClusters = middleClusterNamesDataset->getClusters(); - QVector bottomClusters = bottomClusterNamesDataset->getClusters(); - - if (!mainPointDimensionNames.empty()) { - std::map> combinedClusters = { - {"top", topClusters}, - {"middle", middleClusters}, - {"bottom", bottomClusters} - }; - - QMutex mutex; // Mutex for thread safety - - QtConcurrent::blockingMap(combinedClusters, [&](const auto& pair) { - const auto& hierarchyType = pair.first; - const auto& clusters = pair.second; - - for (const auto& cluster : clusters) { - const auto& clusterName = cluster.getName(); - auto clusterIndices = cluster.getIndices(); - std::sort(clusterIndices.begin(), clusterIndices.end()); - std::map> topSpeciesToGeneExpressionMap; - - QtConcurrent::blockingMap(speciesClusters, [&](const auto& species) { - const auto& speciesName = species.getName(); - auto speciesIndices = species.getIndices(); - std::sort(speciesIndices.begin(), speciesIndices.end()); - - std::vector commonPointsIndices; - std::set_intersection(speciesIndices.begin(), speciesIndices.end(), clusterIndices.begin(), clusterIndices.end(), std::back_inserter(commonPointsIndices)); - - if (commonPointsIndices.empty()) { - //qDebug() << "No common points found"; - return; - } - - std::vector resultContainerShort(commonPointsIndices.size()); - std::vector geneIndexContainer(1); - - QtConcurrent::blockingMap(mainPointDimensionNames, [&](const QString& geneName) { - auto it = std::find(mainPointDimensionNames.begin(), mainPointDimensionNames.end(), geneName); - int geneIndex = (it != mainPointDimensionNames.end()) ? std::distance(mainPointDimensionNames.begin(), it) : -1; - geneIndexContainer[0] = geneIndex; - - const auto& nonSelectionDetails = _clusterGeneMeanExpressionMap[speciesName][geneName]; - int allCellCounts = nonSelectionDetails.first; - float allCellMean = nonSelectionDetails.second; - - mainPointsDataset->populateDataForDimensions(resultContainerShort, geneIndexContainer, commonPointsIndices); - - StatisticsSingle calculateStatisticsShort = calculateStatistics(resultContainerShort); - - float allCellTotal = allCellMean * allCellCounts; - int nonSelectedCells = allCellCounts - calculateStatisticsShort.countVal; - float nonSelectedMean = (nonSelectedCells > 0) ? (allCellTotal - calculateStatisticsShort.meanVal * calculateStatisticsShort.countVal) / nonSelectedCells : 0.0f; - - StatisticsSingle calculateStatisticsNot = { nonSelectedMean, nonSelectedCells }; - int topHierarchyCountValue = (_clusterSpeciesFrequencyMap.find(speciesName) != _clusterSpeciesFrequencyMap.end()) ? _clusterSpeciesFrequencyMap[speciesName]["topCells"] : 0; - int middleHierarchyCountValue = (_clusterSpeciesFrequencyMap.find(speciesName) != _clusterSpeciesFrequencyMap.end()) ? _clusterSpeciesFrequencyMap[speciesName]["topCells"] : 0; - //float topHierarchyFrequencyValue = (topHierarchyCountValue != 0) ? static_cast(calculateStatisticsShort.countVal) / topHierarchyCountValue : 0.0f; - - QMutexLocker locker(&mutex); // Lock the mutex for thread safety - topSpeciesToGeneExpressionMap[speciesName][geneName] = combineStatisticsSingle(calculateStatisticsShort, calculateStatisticsNot, topHierarchyCountValue, middleHierarchyCountValue, middleHierarchyCountValue); - }); - }); - - enum class SelectionOption { - AbsoluteTopN, - PositiveTopN, - NegativeTopN - }; - - auto optionValue = _typeofTopNGenes.getCurrentText(); - SelectionOption option = SelectionOption::AbsoluteTopN; - if (optionValue == "Positive") { - option = SelectionOption::PositiveTopN; - } - else if (optionValue == "Negative") { - option = SelectionOption::NegativeTopN; - } - - std::map>> rankingMap; - - for (const auto& [speciesName, geneMap] : topSpeciesToGeneExpressionMap) { - std::vector> geneExpressionVec; - geneExpressionVec.reserve(geneMap.size()); - for (const auto& [geneName, stats] : geneMap) { - float differenceMeanValue = stats.meanSelected - stats.meanNonSelected; - geneExpressionVec.emplace_back(geneName, differenceMeanValue); - } - - if (option == SelectionOption::AbsoluteTopN) { - std::sort(geneExpressionVec.begin(), geneExpressionVec.end(), [](const auto& a, const auto& b) { - return std::abs(a.second) > std::abs(b.second); - }); - } - else { - std::sort(geneExpressionVec.begin(), geneExpressionVec.end(), [](const auto& a, const auto& b) { - return a.second > b.second; - }); - if (option == SelectionOption::NegativeTopN) { - std::reverse(geneExpressionVec.begin(), geneExpressionVec.end()); - } - } - - for (int i = 0; i < geneExpressionVec.size(); ++i) { - int rank = (option == SelectionOption::NegativeTopN) ? geneExpressionVec.size() - i : i + 1; - QMutexLocker locker(&mutex); // Lock the mutex for thread safety - rankingMap[geneExpressionVec[i].first].emplace_back(speciesName, rank); - } - } - - for (auto& [geneName, speciesRankVec] : rankingMap) { - std::map utilityMap; - for (const auto& [speciesName, rank] : speciesRankVec) { - InitialStatistics tempStats; - tempStats.rankVal = rank; - tempStats.geneName = geneName; - tempStats.meanVal = topSpeciesToGeneExpressionMap[speciesName][geneName].meanSelected; - tempStats.differentialVal = topSpeciesToGeneExpressionMap[speciesName][geneName].meanSelected - topSpeciesToGeneExpressionMap[speciesName][geneName].meanNonSelected; - - tempStats.abundanceTop = (topSpeciesToGeneExpressionMap[speciesName][geneName].abundanceTop != 0) ? topSpeciesToGeneExpressionMap[speciesName][geneName].meanSelected / topSpeciesToGeneExpressionMap[speciesName][geneName].abundanceTop : 0.0f; - - tempStats.abundanceMiddle = (topSpeciesToGeneExpressionMap[speciesName][geneName].abundanceMiddle != 0) ? topSpeciesToGeneExpressionMap[speciesName][geneName].meanSelected / topSpeciesToGeneExpressionMap[speciesName][geneName].abundanceMiddle : 0.0f; - - utilityMap[speciesName] = tempStats; - } - - QMutexLocker locker(&mutex); // Lock the mutex for thread safety - createTreeInitial(speciesDataJson, utilityMap); - - //convert QJsonObjectToString to store in a more space efficientway and then again convert the string to QJSONObject - QString jsonString = QJsonDocument(speciesDataJson).toJson(QJsonDocument::Compact); - - - _precomputedTreesFromTheHierarchy[hierarchyType][clusterName][geneName] = jsonString; - } - } - }); - - } - - else - { - qDebug() << "Datasets are not valid"; - return; - } - - } - else - { - qDebug() << "Datasets are not valid"; - return; - } - - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start).count(); - qDebug() << "Time taken for precomputeTreesFromHierarchy : " + QString::number(duration / 1000.0) + " s"; - //_popupMessageInit.hide(); - //_popupMessageTreeCreationCompletion->show(); - //QApplication::processEvents(); -} - - -void SettingsAction::computeGeneMeanExpressionMap() -{ - if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) - { - return; - } - - - _clusterGeneMeanExpressionMap.clear(); - auto start = std::chrono::high_resolution_clock::now(); - qDebug() << "Computing gene mean expression map"; - - _clusterGeneMeanExpressionMap.clear(); - if (_speciesNamesDataset.getCurrentDataset().isValid() && _mainPointsDataset.getCurrentDataset().isValid()) { - auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); - auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); - if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) { - auto speciesclusters = speciesClusterDatasetFull->getClusters(); - auto mainPointDimensionNames = mainPointDatasetFull->getDimensionNames(); - - QtConcurrent::blockingMap(speciesclusters, [&](const auto& species) { - auto speciesIndices = species.getIndices(); - auto speciesName = species.getName(); - for (int i = 0; i < mainPointDimensionNames.size(); i++) { - auto& geneName = mainPointDimensionNames[i]; - auto geneIndex = { i }; - std::vector resultContainerFull(speciesIndices.size()); - mainPointDatasetFull->populateDataForDimensions(resultContainerFull, geneIndex, speciesIndices); - float fullMean = calculateMean(resultContainerFull); - _clusterGeneMeanExpressionMap[speciesName][geneName] = std::make_pair(speciesIndices.size(), fullMean); - } - }); - - _meanMapComputed = true; - } - } - - - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start).count(); - qDebug() << "Time taken for computeGeneMeanExpressionMap : " + QString::number(duration / 1000.0) + " s"; -} - -void SettingsAction::computeHierarchyAppearanceVector() -{ - if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) - { - return; - } - - auto startTimer = std::chrono::high_resolution_clock::now(); - qDebug() << "computeHierarchyAppearanceVector Start"; - _topHierarchyClusterMap.clear(); - - if (_mainPointsDataset.getCurrentDataset().isValid()) { - auto fullMainDataset = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); - auto numOfPoints = fullMainDataset->getNumPoints(); - auto clusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); - QStringList inclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - if (clusterDataset.isValid()) { - auto clusters = clusterDataset->getClusters(); - if (!clusters.empty()) { - - for (const auto& cluster : clusters) { - - auto clusterName = cluster.getName(); - if (inclusionList.contains(clusterName)) - { - std::vector clusterNamesAppearance(numOfPoints, false); - for (const auto& index : cluster.getIndices()) { - clusterNamesAppearance[index] = true; - } - _topHierarchyClusterMap[clusterName] = clusterNamesAppearance; - } - - - } - } - } - - } - - auto endTimer = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(endTimer - startTimer).count(); - qDebug() << "Time taken for computeHierarchyAppearanceVector : " + QString::number(duration / 1000.0) + " s"; - -} - - -void SettingsAction::computeFrequencyMapForHierarchyItemsChange(QString hierarchyType) -{ - if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked() || hierarchyType.isEmpty()) - { - return; - } - - auto startTimer = std::chrono::high_resolution_clock::now(); - qDebug() << "computeFrequencyMapForHierarchyItemsChange Start for " + hierarchyType; - - if (!_speciesNamesDataset.getCurrentDataset().isValid() || !_mainPointsDataset.getCurrentDataset().isValid()) { - return; - } - - auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); - auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); - auto numOfPoints = mainPointDatasetFull->getNumPoints(); - std::vector clusterNames(numOfPoints, true); - QStringList inclusionList; - mv::Dataset clusterDataset; - - if (hierarchyType == "top" && _topClusterNamesDataset.getCurrentDataset().isValid()) - { - inclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - clusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); - } - /* - else if (hierarchyType == "middle" && _middleClusterNamesDataset.getCurrentDataset().isValid()) - { - inclusionList = _middleHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - clusterDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); - } - else if (hierarchyType == "bottom" && _bottomClusterNamesDataset.getCurrentDataset().isValid()) - { - inclusionList = _bottomHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - clusterDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); - } - */ - if (clusterDataset.isValid()) - { - for (const auto& cluster : clusterDataset->getClusters()) - { - if (!inclusionList.contains(cluster.getName())) - { - for (const auto& index : cluster.getIndices()) - { - clusterNames[index] = false; - } - } - } - } - - if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) - { - auto speciesclusters = speciesClusterDatasetFull->getClusters(); - for (const auto& species : speciesclusters) { - auto speciesIndices = species.getIndices(); - auto speciesName = species.getName(); - int count = std::count_if(speciesIndices.begin(), speciesIndices.end(), [&clusterNames](int index) { - return clusterNames[index]; - }); - - if (hierarchyType == "top") - { - _clusterSpeciesFrequencyMap[speciesName]["topCells"] = count; - } - else if (hierarchyType == "middle") - { - _clusterSpeciesFrequencyMap[speciesName]["middleCells"] = count; - } - else if (hierarchyType == "bottom") - { - _clusterSpeciesFrequencyMap[speciesName]["bottomCells"] = count; - } - } - } - - auto endTimer = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(endTimer - startTimer).count(); - qDebug() << "Time taken for computeFrequencyMapForHierarchyItemsChange for " + hierarchyType + " : " + QString::number(duration / 1000.0) + " s"; -} -/* -void SettingsAction::computeGeneMeanExpressionMapForHierarchyItemsChangeExperimental(QString hierarchyType) -{ - if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) - { - return; - } - auto startTimer = std::chrono::high_resolution_clock::now(); - qDebug() << "computeGeneMeanExpressionMapForHierarchyItemsChange Experimental Start for " + hierarchyType; - if (hierarchyType == "") - { - return; - } - - - if (_speciesNamesDataset.getCurrentDataset().isValid() && _mainPointsDataset.getCurrentDataset().isValid()) { - - auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); - auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); - auto numOfPoints = mainPointDatasetFull->getNumPoints(); - std::vector clusterNames(numOfPoints, true); - bool datasetCheck = false; - QStringList inclusionList; - if (hierarchyType == "top") - { - inclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - if (_topClusterNamesDataset.getCurrentDataset().isValid()) - { - datasetCheck = true; - } - } - else if (hierarchyType == "middle") - { - inclusionList = _middleHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - if (_middleClusterNamesDataset.getCurrentDataset().isValid()) - { - datasetCheck = true; - } - } - else if (hierarchyType == "bottom") - { - inclusionList = _bottomHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - if (_bottomClusterNamesDataset.getCurrentDataset().isValid()) - { - datasetCheck = true; - } - } - - if (datasetCheck) - { - mv::Dataset clusterDataset; - - if (hierarchyType == "top") - { - clusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); - } - else if (hierarchyType == "middle") - { - clusterDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); - } - else if (hierarchyType == "bottom") - { - clusterDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); - } - - for (auto cluster : clusterDataset->getClusters()) - { - auto clusterIndices = cluster.getIndices(); - auto clusterName = cluster.getName(); - if (!inclusionList.contains(clusterName)) - { - for (auto index : clusterIndices) - { - clusterNames[index] = false; - } - } - - } - } - - - - if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) - { - auto speciesclusters = speciesClusterDatasetFull->getClusters(); - auto mainPointDimensionNames = mainPointDatasetFull->getDimensionNames(); - for (auto species : speciesclusters) { - auto speciesIndices = species.getIndices(); - auto speciesName = species.getName(); - std::vector indices; - - // Loop through all species indices to determine if they are in respective clusters - for (int i = 0; i < speciesIndices.size(); ++i) { - // Check if the current species index is present in the cluster and only include those that are true - if (std::find(clusterNames.begin(), clusterNames.end(), speciesIndices[i]) != clusterNames.end()) { - indices.push_back(i); - } - - } - - for (int i = 0; i < mainPointDimensionNames.size(); i++) { - auto& geneName = mainPointDimensionNames[i]; - auto geneIndex = { i }; - - - - std::vector resultContainer(indices.size()); - mainPointDatasetFull->populateDataForDimensions(resultContainer, geneIndex, indices); - float topMean = calculateMean(resultContainer); - - if (hierarchyType == "top") - { - _clusterGeneMeanExpressionMap[speciesName][geneName]["topCells"] = std::make_pair(indices.size(), topMean); - } - else if (hierarchyType == "middle") - { - _clusterGeneMeanExpressionMap[speciesName][geneName]["middleCells"] = std::make_pair(indices.size(), topMean); - } - else if (hierarchyType == "bottom") - { - _clusterGeneMeanExpressionMap[speciesName][geneName]["bottomCells"] = std::make_pair(indices.size(), topMean); - } - } - - } - - - } - } - auto endTimer = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(endTimer - startTimer).count(); - qDebug() << "Time taken for computeGeneMeanExpressionMapForHierarchyItemsChangeExperimental for " + hierarchyType + " : " + QString::number(duration / 1000.0) + " s"; - -} -void SettingsAction::computeGeneMeanExpressionMapExperimental() -{ - if (_mapForHierarchyItemsChangeMethodStopForProjectLoadBlocker.isChecked()) - { - return; - } - auto start = std::chrono::high_resolution_clock::now(); - qDebug() << "Computing gene mean expression map"; - - - _clusterGeneMeanExpressionMap.clear(); - - if (_speciesNamesDataset.getCurrentDataset().isValid() && _mainPointsDataset.getCurrentDataset().isValid()) { - - auto speciesClusterDatasetFull = mv::data().getDataset(_speciesNamesDataset.getCurrentDataset().getDatasetId()); - auto mainPointDatasetFull = mv::data().getDataset(_mainPointsDataset.getCurrentDataset().getDatasetId()); - auto numOfPoints = mainPointDatasetFull->getNumPoints(); - std::vector topClusterNames(numOfPoints, true); - std::vector middleClusterNames(numOfPoints, true); - std::vector bottomClusterNames(numOfPoints, true); - QStringList topInclusionList = _topHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - QStringList middleInclusionList = _middleHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - QStringList bottomInclusionList = _bottomHierarchyClusterNamesFrequencyInclusionList.getSelectedOptions(); - if (_topClusterNamesDataset.getCurrentDataset().isValid() && _middleClusterNamesDataset.getCurrentDataset().isValid() && _bottomClusterNamesDataset.getCurrentDataset().isValid()) - - { - auto topClusterDataset = mv::data().getDataset(_topClusterNamesDataset.getCurrentDataset().getDatasetId()); - auto middleClusterDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); - auto bottomClusterDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); - - auto processCluster = [&](const Clusters& dataset, std::vector& clusterNames) { - for (const auto& cluster : dataset.getClusters()) { - auto clusterIndices = cluster.getIndices(); - auto clusterName = cluster.getName(); - if (!topInclusionList.contains(clusterName)) { - for (auto index : clusterIndices) { - if (index < clusterNames.size()) { - clusterNames[index] = false; - } - } - } - } - }; - - QFuture topFuture = QtConcurrent::run([&]() { processCluster(*topClusterDataset, topClusterNames); }); - QFuture middleFuture = QtConcurrent::run([&]() { processCluster(*middleClusterDataset, middleClusterNames); }); - QFuture bottomFuture = QtConcurrent::run([&]() { processCluster(*bottomClusterDataset, bottomClusterNames); }); - - topFuture.waitForFinished(); - middleFuture.waitForFinished(); - bottomFuture.waitForFinished(); - } - - - - // Ensure that the types match - QMutex mapMutex; // Mutex to protect shared access to _clusterGeneMeanExpressionMap - - if (speciesClusterDatasetFull.isValid() && mainPointDatasetFull.isValid()) { - auto speciesclusters = speciesClusterDatasetFull->getClusters(); - auto mainPointDimensionNames = mainPointDatasetFull->getDimensionNames(); - - // Parallel processing of species clusters - QtConcurrent::blockingMap(speciesclusters, [&](const auto& species) { - auto speciesIndices = species.getIndices(); - auto speciesName = species.getName(); - - std::vector topIndices; - std::vector middleIndices; - std::vector bottomIndices; - - // Determine cluster membership for the species - for (uint32_t i = 0; i < speciesIndices.size(); ++i) { - if (std::binary_search(topClusterNames.begin(), topClusterNames.end(), speciesIndices[i])) { - topIndices.push_back(i); - } - if (std::binary_search(middleClusterNames.begin(), middleClusterNames.end(), speciesIndices[i])) { - middleIndices.push_back(i); - } - if (std::binary_search(bottomClusterNames.begin(), bottomClusterNames.end(), speciesIndices[i])) { - bottomIndices.push_back(i); - } - } - - // Parallel processing of gene expressions within each species - QtConcurrent::blockingMap(mainPointDimensionNames, [&](const auto& geneName) { - // Manually find the index of the geneName - int geneIndex = std::distance(mainPointDimensionNames.begin(), - std::find(mainPointDimensionNames.begin(), mainPointDimensionNames.end(), geneName)); - - if (geneIndex == mainPointDimensionNames.size()) { - // Handle case where geneName is not found if necessary - return; // Skip processing if the index is invalid - } - - auto processCells = [&](const std::vector& indices, const QString& cellType) { - std::vector resultContainer(indices.size()); - mainPointDatasetFull->populateDataForDimensions(resultContainer, std::vector{geneIndex}, indices); - float mean = calculateMean(resultContainer); - QMutexLocker locker(&mapMutex); - _clusterGeneMeanExpressionMap[speciesName][geneName][cellType] = std::make_pair(indices.size(), mean); - }; - - processCells(speciesIndices, "allCells"); - processCells(topIndices, "topCells"); - processCells(middleIndices, "middleCells"); - processCells(bottomIndices, "bottomCells"); - }); - }); - - _meanMapComputed = true; - } - - - - } - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start).count(); - qDebug() << "\n\n++++++++++++++++++Time taken for computeGeneMeanExpressionMap : " + QString::number(duration / 1000.0) + " s"; - -} -*/ -void SettingsAction::findTopNGenesPerCluster() { - - int n = _topNGenesFilter.getValue(); - - if (_clusterNameToGeneNameToExpressionValue.empty() || n <= 0) { - return; - } - - // startCodeTimer("findTopNGenesPerCluster"); - - enum class SelectionOption { - AbsoluteTopN, - PositiveTopN, - NegativeTopN - }; - auto optionValue = _typeofTopNGenes.getCurrentText(); - SelectionOption option = SelectionOption::AbsoluteTopN; - if (optionValue == "Positive") { - option = SelectionOption::PositiveTopN; - } - else if (optionValue == "Negative") { - option = SelectionOption::NegativeTopN; - } - - _uniqueReturnGeneList.clear(); - std::map> geneAppearanceCounter; - std::map>> rankingMap; - - for (const auto& outerPair : _clusterNameToGeneNameToExpressionValue) { - auto speciesName = outerPair.first; - std::vector> geneExpressionVec; - geneExpressionVec.reserve(outerPair.second.size()); - for (const auto& innerPair : outerPair.second) { - auto geneName = innerPair.first; - auto differenceMeanValue = innerPair.second.meanSelected - innerPair.second.meanNonSelected; - geneExpressionVec.push_back(std::make_pair(geneName, differenceMeanValue)); - } - - // Sort the geneExpressionVec based on the mean value from highest to lowest - std::sort(geneExpressionVec.begin(), geneExpressionVec.end(), [](const auto& a, const auto& b) { - return a.second > b.second; - }); - - switch (option) { - case SelectionOption::AbsoluteTopN: { - std::sort(geneExpressionVec.begin(), geneExpressionVec.end(), [](const auto& a, const auto& b) { - return std::abs(a.second) > std::abs(b.second); - }); - for (int i = 0; i < geneExpressionVec.size(); ++i) { - if (i < n) { - _uniqueReturnGeneList.insert(geneExpressionVec[i].first); - //check if the selected counter in the mpa is greater than 0 - if (outerPair.second.find(geneExpressionVec[i].first)->second.meanSelected > 0) { - geneAppearanceCounter[geneExpressionVec[i].first].push_back(speciesName); - } - - //geneAppearanceCounter[geneExpressionVec[i].first].push_back(speciesName); - } - - rankingMap[geneExpressionVec[i].first].emplace_back(speciesName, i + 1); - - } - - - break; - } - case SelectionOption::PositiveTopN: { - for (int i = 0; i < geneExpressionVec.size(); ++i) { - if (i < n) { - _uniqueReturnGeneList.insert(geneExpressionVec[i].first); - if (outerPair.second.find(geneExpressionVec[i].first)->second.meanSelected > 0) { - geneAppearanceCounter[geneExpressionVec[i].first].push_back(speciesName); - } - - //geneAppearanceCounter[geneExpressionVec[i].first].push_back(speciesName); - } - rankingMap[geneExpressionVec[i].first].emplace_back(speciesName, i + 1); // Adding rank, incremented by 1 - } - break; - } - case SelectionOption::NegativeTopN: { - std::reverse(geneExpressionVec.begin(), geneExpressionVec.end()); // Reverse to get the lowest values - for (int i = 0; i < geneExpressionVec.size(); ++i) { - if (i < n) { - _uniqueReturnGeneList.insert(geneExpressionVec[i].first); - if (outerPair.second.find(geneExpressionVec[i].first)->second.meanSelected > 0) { - geneAppearanceCounter[geneExpressionVec[i].first].push_back(speciesName); - } - - //geneAppearanceCounter[geneExpressionVec[i].first].push_back(speciesName); - } - rankingMap[geneExpressionVec[i].first].emplace_back(speciesName, geneExpressionVec.size() - i); // Corrected rank calculation - } - break; - } - } - } - - //iterate std::map>> rankingMap; - // Iterating over the map - if (_performGeneTableTsneAction.isChecked()) { - std::vector rankOrder; - - std::unordered_map> geneSimilarityMap; - std::unordered_set speciesSet; - std::unordered_set geneSet; - - for (const auto& item : rankingMap) { - const QString& gene = item.first; - std::unordered_map rankCounter; - for (const auto& pair : item.second) { - const QString& species = pair.first; - const float rank = (pair.second <= n) ? 1.0f : 0.0f; // Use float directly - rankCounter[species] = rank; - speciesSet.insert(species); - } - geneSimilarityMap[gene] = std::move(rankCounter); - geneSet.insert(gene); - } - _geneOrder.clear(); - std::vector speciesOrder(speciesSet.begin(), speciesSet.end()); - _geneOrder = std::vector(geneSet.begin(), geneSet.end()); - rankOrder.resize(_geneOrder.size() * speciesOrder.size(), 0.0f); // Initialize with 0.0f for clarity - - std::unordered_map speciesIndexMap; - for (int i = 0; i < speciesOrder.size(); ++i) { - speciesIndexMap[speciesOrder[i]] = i; - } - - for (int geneIndex = 0; geneIndex < _geneOrder.size(); ++geneIndex) { - const QString& gene = _geneOrder[geneIndex]; - const auto& speciesRanks = geneSimilarityMap[gene]; - for (const auto& speciesRank : speciesRanks) { - const QString& species = speciesRank.first; - const float rank = speciesRank.second; // Already a float, no need to cast - int speciesIndex = speciesIndexMap[species]; - rankOrder[geneIndex * speciesOrder.size() + speciesIndex] = rank; - } - } - - QString pointDataId = _geneSimilarityPoints->getId(); - int pointDimSize = static_cast(speciesOrder.size()); - int pointIndicesSize = static_cast(_geneOrder.size()); - - if (_selectedPointsTSNEDatasetForGeneTable.isValid()) - { - auto runningAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Running")); - - if (runningAction) - { - - if (runningAction->isChecked()) - { - auto stopAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Stop")); - if (stopAction) - { - stopAction->trigger(); - //std::this_thread::sleep_for(std::chrono::seconds(5)); - } - } - - } - mv::data().removeDataset(_selectedPointsTSNEDatasetForGeneTable); - } - - populatePointData(pointDataId, rankOrder, pointIndicesSize, pointDimSize, speciesOrder); - - mv::plugin::AnalysisPlugin* analysisPlugin; - auto scatterplotModificationsGeneSimilarity = [this]() { - if (_selectedPointsTSNEDatasetForGeneTable.isValid()) { - auto scatterplotViewFactory = mv::plugins().getPluginFactory("Scatterplot View"); - mv::gui::DatasetPickerAction* colorDatasetPickerAction; - mv::gui::DatasetPickerAction* pointDatasetPickerAction; - mv::gui::ViewPluginSamplerAction* samplerActionAction; - if (scatterplotViewFactory) { - for (auto plugin : mv::plugins().getPluginsByFactory(scatterplotViewFactory)) { - if (plugin->getGuiName() == "Scatterplot Cell Selection Overview") { - pointDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Position")); - if (pointDatasetPickerAction) { - pointDatasetPickerAction->setCurrentText(""); - - pointDatasetPickerAction->setCurrentDataset(_selectedPointsTSNEDatasetForGeneTable); - - colorDatasetPickerAction = dynamic_cast(plugin->findChildByPath("Settings/Datasets/Color")); - if (colorDatasetPickerAction) - { - colorDatasetPickerAction->setCurrentText(""); - - if (_geneSimilarityClusterColoring.isValid()) - { - colorDatasetPickerAction->setCurrentDataset(_geneSimilarityClusterColoring); - auto legendViewFactory = mv::plugins().getPluginFactory("ChartLegend View"); - if (legendViewFactory) - { - for (auto legendPlugin : mv::plugins().getPluginsByFactory(legendViewFactory)) - { - if (legendPlugin->getGuiName() == "Legend View") - { - //legendPlugin->printChildren(); - auto legendDatasetPickerAction = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster dataset")); - if (legendDatasetPickerAction) - { - legendDatasetPickerAction->setCurrentDataset(_geneSimilarityClusterColoring); - } - auto chartTitle = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Chart Title")); - if (chartTitle) - { - chartTitle->setString("Cell types"); - } - /* - auto selectionColor = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Selection color")); - if (selectionColor) - { - selectionColor->setColor(QColor(53, 126, 199)); - } - auto selectionStringDelimiter = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Delimiter")); - if (selectionStringDelimiter) - { - selectionStringDelimiter->setString(","); - } - - auto selectionClustersString = dynamic_cast(legendPlugin->findChildByPath("ChartLegendViewPlugin Chart/Color Options/Cluster Selection string")); - if (selectionClustersString) - { - selectionClustersString->setString(""); //TODO - } - */ - } - } - } - - } - } - - samplerActionAction = plugin->findChildByPath("Sampler"); - - if (samplerActionAction) - { - samplerActionAction->setHtmlViewGeneratorFunction([this](const ViewPluginSamplerAction::SampleContext& toolTipContext) -> QString { - QString clusterDatasetId = _speciesNamesDataset.getCurrentDataset().getDatasetId(); - return generateTooltip(toolTipContext, clusterDatasetId, true, "GlobalPointIndices"); - }); - } - } - } - } - } - } - - }; - - - - { - //startCodeTimer("Part10"); - analysisPlugin = mv::plugins().requestPlugin("tSNE Analysis", { _geneSimilarityPoints }); - if (!analysisPlugin) { - qDebug() << "Could not find create TSNE Analysis"; - return; - } - _selectedPointsTSNEDatasetForGeneTable = analysisPlugin->getOutputDataset(); - int groupID2 = 10 * 3; - _selectedPointsTSNEDatasetForGeneTable->setGroupIndex(groupID2); - if (_selectedPointsTSNEDatasetForGeneTable.isValid()) - { - //_selectedPointsTSNEDatasetForGeneTable->printChildren(); - bool skip = false; - int perplexity = std::min(static_cast(_geneOrder.size()), _tsnePerplexity.getValue()); - if (perplexity < 5) - { - qDebug() << "Perplexity is less than 5"; - skip = true; - //_startComputationTriggerAction.setDisabled(false); - } - if (!skip) - { - if (perplexity != _tsnePerplexity.getValue()) - { - _tsnePerplexity.setValue(perplexity); - } - - auto perplexityAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/Perplexity")); - if (perplexityAction) - { - //qDebug() << "Perplexity: Found"; - perplexityAction->setValue(perplexity); - } - else - { - qDebug() << "Perplexity: Not Found"; - } - - QString knnAlgorithmValue = _performGeneTableTsneKnn.getCurrentText(); - QString distanceMetricValue = _performGeneTableTsneDistance.getCurrentText(); - if (knnAlgorithmValue != "") - { - auto knnAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/kNN Algorithm")); - if (knnAction) - { - //qDebug() << "Knn: Found"; - try { - knnAction->setCurrentText(knnAlgorithmValue); - } - catch (const std::exception& e) { - qDebug() << "An exception occurred in setting knn value: " << e.what(); - } - } - else - { - qDebug() << "Knn: Not Found"; - } - } - if (distanceMetricValue != "") - { - auto distanceAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/Distance metric")); - if (distanceAction) - { - //qDebug() << "Distance: Found"; - try { - distanceAction->setCurrentText(distanceMetricValue); - } - catch (const std::exception& e) { - qDebug() << "An exception occurred in setting distance value: " << e.what(); - } - } - else - { - qDebug() << "Distance: Not Found"; - } - } - - scatterplotModificationsGeneSimilarity(); - - auto startAction = dynamic_cast(_selectedPointsTSNEDatasetForGeneTable->findChildByPath("TSNE/TsneComputationAction/Start")); - if (startAction) { - - startAction->trigger(); - - analysisPlugin->getOutputDataset()->setSelectionIndices({}); - } - } - } - //stopCodeTimer("Part10"); - } - - std::vector selectedIndices; - std::vector nonselectedIndices; - selectedIndices.reserve(_geneOrder.size()); // Pre-allocate memory - nonselectedIndices.reserve(_geneOrder.size()); // Pre-allocate memory - for (int i = 0; i < _geneOrder.size(); i++) - { - if (_uniqueReturnGeneList.find(_geneOrder[i]) != _uniqueReturnGeneList.end()) - { - selectedIndices.push_back(i); - } - else - { - nonselectedIndices.push_back(i); - } - } - QString clusterDataId = _geneSimilarityClusterColoring->getId(); - QColor selectedColor = QColor("#00A2ED"); - QColor nonSelectedColor = QColor("#ff5d12"); - std::map>> selectedClusterMap; - selectedClusterMap["TopNSelectedGenes"] = { selectedColor, selectedIndices }; - selectedClusterMap["NonTopNGenes"] = { nonSelectedColor, nonselectedIndices }; - - populateClusterData(clusterDataId, selectedClusterMap); - } - - //stopCodeTimer("findTopNGenesPerCluster"); - QVariant returnedmodel = createModelFromData(_clusterNameToGeneNameToExpressionValue, geneAppearanceCounter, rankingMap, n); - - setModifiedTriggeredData(returnedmodel); - _selectedGene.setString(""); - //return returnedmodel; -} - -void SettingsAction::clearTemporaryDatasetHandles() -{ - _selectedPointsTSNEDataset = Dataset(); - _selectedPointsDataset = Dataset(); - _selectedPointsEmbeddingDataset = Dataset(); - _filteredUMAPDatasetPoints = Dataset(); - _filteredUMAPDatasetColors = Dataset(); - _filteredUMAPDatasetClusters = Dataset(); - _tsneDatasetExpressionColors = Dataset(); - _geneSimilarityPoints = Dataset(); - - _tsneDatasetSpeciesColors = Dataset(); - _tsneDatasetClusterColors = Dataset(); - _geneSimilarityClusterColoring = Dataset(); -} - -void SettingsAction::removeDatasets(int groupId) -{ - auto allDatasets = mv::data().getAllDatasets(); - - // id -> dataset pointer (NO COPYING) - QHash> idToDataset; - idToDataset.reserve(allDatasets.size()); - - for (const auto& ds : allDatasets) { - if (ds->getGroupIndex() == groupId) { - idToDataset.insert(ds->getId(), ds); - } - } - - // Cache depth (memoization) - QHash depthCache; - depthCache.reserve(idToDataset.size()); - - std::function depthOf = - [&](const QString& id) -> int - { - auto it = depthCache.find(id); - if (it != depthCache.end()) - return it.value(); - - int depth = 0; - - auto ds = idToDataset.value(id); - auto parent = ds->getParent(); - - if (parent.isValid()) { - QString parentId = parent->getId(); - - if (idToDataset.contains(parentId)) { - depth = 1 + depthOf(parentId); - } - } - - depthCache.insert(id, depth); - return depth; - }; - - // Build list of ids - QVector ids; - ids.reserve(idToDataset.size()); - - for (auto it = idToDataset.begin(); it != idToDataset.end(); ++it) { - ids.push_back(it.key()); - } - - // Compute all depths (O(N)) - for (const auto& id : ids) { - depthOf(id); - } - - // Sort deepest first (critical step) - std::sort(ids.begin(), ids.end(), - [&](const QString& a, const QString& b) { - return depthCache[a] > depthCache[b]; - }); - - // Delete in correct order - for (const auto& id : ids) { - auto ds = idToDataset.value(id); - if (ds.isValid()) { - qDebug() << "Deleting:" << ds->getId() << "with name:" << ds->getGuiName() - << "depth:" << depthCache[id]; - - mv::data().removeDataset(ds); - } - } -} -QVariant SettingsAction::createModelFromData(const std::map>& map, const std::map>& geneCounter, const std::map>>& rankingMap, const int& n) { - - if (map.empty() || _totalGeneList.empty()) { - return QVariant(); - } - //startCodeTimer("createModelFromData"); - QStandardItemModel* model = new QStandardItemModel(); - _initColumnNames = { "ID", "Species \nAppearance", "Gene Appearance Species Names", "Statistics" }; - model->setHorizontalHeaderLabels(_initColumnNames); - - QStringList headers = _initColumnNames; - _hiddenShowncolumns.setOptions(headers); - _hiddenShowncolumns.setSelectedOptions({ headers[0], headers[1] }); - - for (const auto& gene : _totalGeneList) { - QList row; - std::vector numbers; - numbers.reserve(map.size()); // Reserve capacity based on map size - std::map statisticsValuesForSpeciesMap; - - for (const auto& [speciesName, innerMap] : map) { - auto it = innerMap.find(gene); - if (it != innerMap.end()) { - float value = it->second.meanSelected; - numbers.push_back(value); - statisticsValuesForSpeciesMap[speciesName] = it->second; - } - } - - row.push_back(new QStandardItem(gene)); // ID(string) should sort by string - - std::map rankcounter; - if (auto rankit = rankingMap.find(gene); rankit != rankingMap.end()) { - // Assuming rankit->second is of type std::vector> - for (const auto& pair : rankit->second) { - rankcounter[pair.first] = pair.second; - } - } - - QString speciesGeneAppearancesComb; - int count = 0; - if (auto it = geneCounter.find(gene); it != geneCounter.end()) { - const auto& speciesDetails = it->second; - count = static_cast(speciesDetails.size()); - QStringList speciesNames; - for (const auto& speciesDetail : speciesDetails) { - speciesNames << speciesDetail; - } - speciesGeneAppearancesComb = speciesNames.join(";"); - } - auto* countItem = new QStandardItem(); // Gene Appearances (int) should sort by int - countItem->setData(count, Qt::DisplayRole); - countItem->setData(count, Qt::UserRole); // Use Qt::UserRole or another custom role for sorting by integer - row.push_back(countItem); - - //row.push_back(new QStandardItem(QString::number(count))); // Gene Appearances (int) should sort by int - row.push_back(new QStandardItem(speciesGeneAppearancesComb)); // Gene Appearance Species Names (string) should sort by string - - QString formattedStatistics; - for (const auto& [species, stats] : statisticsValuesForSpeciesMap) { - formattedStatistics += QString("Species: %1, Rank: %2, AbundanceTop: %3, AbundanceMiddle: %4, CountAbundanceNumerator: %5, MeanSelected: %6, CountSelected: %7, MeanNotSelected: %8, CountNotSelected: %9;\n")//, MeanAll: %7, CountAll: %8 - .arg(species) - .arg(rankcounter[species]) - .arg(stats.abundanceTop) - .arg(stats.abundanceMiddle) - .arg(stats.countAbundanceNumerator) - .arg(stats.meanSelected, 0, 'f', 2) - .arg(stats.countSelected) - .arg(stats.meanNonSelected, 0, 'f', 2) - .arg(stats.countNonSelected) - //.arg(stats.meanAll, 0, 'f', 2) - //.arg(stats.countAll) - ; - } - row.push_back(new QStandardItem(formattedStatistics)); // Statistics (string) should sort by string - model->appendRow(row); - } - - //stopCodeTimer("createModelFromData"); - - return QVariant::fromValue(model); - -} -void SettingsAction::createClusterPositionMap() -{ - _clusterPositionMap; -} -QStringList SettingsAction::getSystemModeColor() { - // Get the application palette - QPalette palette = QApplication::palette(); - - // Check the color of the window text to determine if the system is in dark mode or light mode - // Assuming dark mode has lighter text (e.g., white) and light mode has darker text (e.g., black) - if (palette.color(QPalette::WindowText).lightness() < 128) { - // Light mode - return { "#FFFFFF","#000000" }; // White - } - else { - // Dark mode - return { "#000000","#FFFFFF" }; // Black - } -} - - -void SettingsAction::exportTableViewToCSVForGenes(QTableView* tableView) { - if (!tableView) { - qWarning() << "TableView is null."; - return; - } - - QAbstractItemModel* model = tableView->model(); - if (!model) { - qWarning() << "TableView model is null."; - return; - } - - QString filePath = QFileDialog::getSaveFileName(nullptr, "Save CSV", "", "CSV Files (*.csv);;All Files (*)"); - if (filePath.isEmpty()) { - qWarning() << "No file selected for saving."; - return; - } - - QFile file(filePath); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - qWarning() << "Could not open file for writing: " << filePath; - return; - } - - QTextStream stream(&file); - - if (model->columnCount() == 4) - { - //ID,Species Appearance,Gene Appearance Species Names,Statistics - QString headerString = "ID,Species Appearance,Gene Appearance Species Names"; - stream << headerString; - stream << "\n"; - - for (int row = 0; row < model->rowCount(); ++row) { - for (int col = 0; col < model->columnCount(); ++col) - { - if(col < 3){ - if (col > 0) { - stream << ","; - } - - stream << model->data(model->index(row, col)).toString(); - } - } - stream << "\n"; - } - } - else - { - // Write header - for (int col = 0; col < model->columnCount(); ++col) { - if (col > 0) { - stream << ","; - } - stream << model->headerData(col, Qt::Horizontal).toString(); - } - stream << "\n"; - - // Write data - for (int row = 0; row < model->rowCount(); ++row) { - for (int col = 0; col < model->columnCount(); ++col) { - if (col > 0) { - stream << ","; - } - stream << model->data(model->index(row, col)).toString(); - } - stream << "\n"; - } +std::unordered_set toStringSet(const QStringList& values) { + std::unordered_set result; + result.reserve(values.size()); + for (const auto& value : values) { + result.insert(value); } - - - file.close(); + return result; } -QString computeMapFromStatistics(QString geneName, QStringList geneAppearanceSpeciesNamesList, QStringList statisticsList) -{ - QString finalString = ""; - - for (int i = 0; i < statisticsList.size(); i++) - { - QString tempString = statisticsList[i]; - QStringList pairs = tempString.split(", "); +template +std::vector intersectSortedIndicesToInt(const LeftContainer& leftIndices, const RightContainer& rightIndices) { + std::vector result; + result.reserve(std::min(leftIndices.size(), rightIndices.size())); - QString speciesName = ""; - QString rank = ""; - QString abundanceTop = ""; - QString abundanceMiddle = ""; - QString countAbundanceNumerator = ""; - QString meanSelected = ""; - QString countSelected = ""; - QString meanNotSelected = ""; - QString countNotSelected = ""; + auto leftIt = leftIndices.begin(); + auto rightIt = rightIndices.begin(); - for (const QString& pair : pairs) { - QStringList keyValue = pair.split(": "); - if (keyValue.size() == 2) { - QString key = keyValue[0].trimmed(); - QString value = keyValue[1].trimmed(); + while (leftIt != leftIndices.end() && rightIt != rightIndices.end()) { + const int leftValue = static_cast(*leftIt); + const int rightValue = static_cast(*rightIt); - if (key == "Species") { - speciesName = value; - } - else if (key == "Rank") { - rank = value; - } - else if (key == "AbundanceTop") { - abundanceTop = value; - } - else if (key == "AbundanceMiddle") { - abundanceMiddle = value; - } - else if (key == "CountAbundanceNumerator") { - countAbundanceNumerator = value; - } - else if (key == "MeanSelected") { - meanSelected = value; - } - else if (key == "CountSelected") { - countSelected = value; - } - else if (key == "MeanNotSelected") { - meanNotSelected = value; - } - else if (key == "CountNotSelected") { - countNotSelected = value; - } - } + if (leftValue < rightValue) { + ++leftIt; } - - finalString += geneName; - if (geneAppearanceSpeciesNamesList.contains(speciesName)) { - finalString += ", True"; + else if (rightValue < leftValue) { + ++rightIt; } else { - finalString += ", False"; - } - finalString += ", " + speciesName; - finalString += ", " + QString::number(meanSelected.toFloat() - meanNotSelected.toFloat()); - finalString += ", " + rank; - finalString += ", " + abundanceTop; - finalString += ", " + abundanceMiddle; - finalString += ", " + countSelected; - finalString += ", " + meanSelected; - finalString += ", " + countNotSelected; - finalString += ", " + meanNotSelected; - if (i < statisticsList.size() - 1) { - finalString += "\n"; - } - } - - return finalString; -} - - -void SettingsAction::exportTableViewToCSVPerGene(QTableView* tableView) { - if (!tableView) { - qWarning() << "TableView is null."; - return; - } - - QAbstractItemModel* model = tableView->model(); - if (!model) { - qWarning() << "TableView model is null."; - return; - } - - QString filePath = QFileDialog::getSaveFileName(nullptr, "Save CSV", "", "CSV Files (*.csv);;All Files (*)"); - if (filePath.isEmpty()) { - qWarning() << "No file selected for saving."; - return; - } - - QFile file(filePath); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - qWarning() << "Could not open file for writing: " << filePath; - return; - } - - QTextStream stream(&file); - - // Find the row index of the selected gene for column 0 - int geneRowIndex = -1; - for (int row = 0; row < model->rowCount(); ++row) { - if (model->data(model->index(row, 0)).toString() == _selectedGene.getString()) { - geneRowIndex = row; - break; - } - } - - - qDebug() << "Gene row index: " << geneRowIndex; - - // If the gene row index is found, write the matching row - - if (model->columnCount()==4) { - //ID,Species Appearance,Gene Appearance Species Names,Statistics - QString ergicString = "Fraction in "+ _topSelectedHierarchyStatus.getString(); - QStringList headers = { "Gene", "Species Appearance", "Species", "Mean Gene Differential Expression", "Gene Appearance Rank", "Fraction in Neuronal", ergicString ,"Count of Selected", "Mean Gene Expression of Selected", "Count of Non Selected","Mean Gene Expression of Non Selected" }; - QString headerNames = headers.join(","); - stream << headerNames; - stream << "\n"; - - if (geneRowIndex != -1) - { - QString geneName = model->data(model->index(geneRowIndex, 0)).toString(); - QString speciesAppearance = model->data(model->index(geneRowIndex, 1)).toString(); - QString geneAppearanceSpeciesNames = model->data(model->index(geneRowIndex, 2)).toString(); - QStringList geneAppearanceSpeciesNamesList = geneAppearanceSpeciesNames.split(";"); - QString statistics = model->data(model->index(geneRowIndex, 3)).toString(); - QStringList statisticsList = statistics.split("\n"); - if (!statisticsList.isEmpty() && statisticsList.last().isEmpty()) { - statisticsList.removeLast(); - } - QString finalString=computeMapFromStatistics(geneName, geneAppearanceSpeciesNamesList, statisticsList); - stream << finalString; - - } - else - { - for (int row = 0; row < model->rowCount(); ++row) { - QString geneName = model->data(model->index(row, 0)).toString(); - QString speciesAppearance = model->data(model->index(row, 1)).toString(); - QString geneAppearanceSpeciesNames = model->data(model->index(row, 2)).toString(); - QStringList geneAppearanceSpeciesNamesList = geneAppearanceSpeciesNames.split(";"); - QString statistics = model->data(model->index(row, 3)).toString(); - QStringList statisticsList = statistics.split("\n"); - if (!statisticsList.isEmpty() && statisticsList.last().isEmpty()) { - statisticsList.removeLast(); - } - QString finalString = computeMapFromStatistics(geneName, geneAppearanceSpeciesNamesList, statisticsList); - stream << finalString; - if (row < model->rowCount() - 1) { - stream << "\n"; - } - } - } - - - } - else - { - // Write header - for (int col = 0; col < model->columnCount(); ++col) { - if (col > 0) { - stream << ","; - } - QString headerVal = model->headerData(col, Qt::Horizontal).toString(); - // Remove all occurrences of "\n" from the headerVal - headerVal.replace("\n", ""); - stream << headerVal; - } - stream << "\n"; - - - - for (int row = 0; row < model->rowCount(); ++row) { - for (int col = 0; col < model->columnCount(); ++col) { - if (col > 0) { - stream << ","; - } - stream << model->data(model->index(row, col)).toString(); - } - stream << "\n"; - } - } - - file.close(); -} - -void SettingsAction::populatePointDataConcurrently(QString datasetId, const std::vector& pointVector, int numPoints, int numDimensions, std::vector dimensionNames) -{ - (void)QtConcurrent::run([this, datasetId, pointVector, numPoints, numDimensions, dimensionNames]() { - auto pointDataset = mv::data().getDataset(datasetId); - - if (pointDataset.isValid()) - { - pointDataset->setSelectionIndices({}); - if (!pointVector.empty() && numPoints > 0 && numDimensions > 0) { - pointDataset->setData(pointVector.data(), numPoints, numDimensions); - pointDataset->setDimensionNames(dimensionNames); - mv::events().notifyDatasetDataChanged(pointDataset); - } - } - }); -} -void SettingsAction::enableActions() -{ - //_startComputationTriggerAction.setDisabled(false); - _topNGenesFilter.setDisabled(false); - _typeofTopNGenes.setDisabled(false); - _clusterCountSortingType.setDisabled(false); - _scatterplotReembedColorOption.setDisabled(false); - _applyLogTransformation.setDisabled(false); - _toggleScatterplotSelection.setDisabled(false); - _usePreComputedTSNE.setDisabled(false); - _tsnePerplexity.setDisabled(false); - _performGeneTableTsnePerplexity.setDisabled(false); - _performGeneTableTsneKnn.setDisabled(false); - _performGeneTableTsneDistance.setDisabled(false); - _performGeneTableTsneTrigger.setDisabled(false); - _computeTreesToDisplayFromHierarchy.setDisabled(false); - _referenceTreeDataset.setDisabled(false); - _mainPointsDataset.setDisabled(false); - _embeddingDataset.setDisabled(false); - _speciesNamesDataset.setDisabled(false); - _bottomClusterNamesDataset.setDisabled(false); - _middleClusterNamesDataset.setDisabled(false); - _topClusterNamesDataset.setDisabled(false); - _speciesExplorerInMap.setDisabled(false); - _topHierarchyClusterNamesFrequencyInclusionList.setDisabled(false); - _speciesExplorerInMapTrigger.setDisabled(false); - _saveGeneTable.setDisabled(false); - _saveSpeciesTable.setDisabled(false); - _revertRowSelectionChangesToInitial.setDisabled(false); - _scatterplotEmbeddingPointsUMAPOption.setDisabled(false); - _selectedSpeciesVals.setDisabled(false); - _clusterOrderHierarchy.setDisabled(false); - _rightClickedCluster.setDisabled(false); - _topSelectedHierarchyStatus.setDisabled(false); - _clearRightClickedCluster.setDisabled(false); - _statusColorAction.setDisabled(false); - _searchBox->setDisabled(false); - enableDisableButtonsAutomatically(); - if (_statusColorAction.getString() == "C") - { - _startComputationTriggerAction.setDisabled(true); - } - else - { - _startComputationTriggerAction.setDisabled(false); - } - _toggleScatterplotSelection.setChecked(true); - QApplication::processEvents(); -} -void SettingsAction::disableActions() -{ - _statusColorAction.setString("R"); - _clearRightClickedCluster.trigger(); - _startComputationTriggerAction.setDisabled(true); - _topNGenesFilter.setDisabled(true); - _typeofTopNGenes.setDisabled(true); - _clusterCountSortingType.setDisabled(true); - _scatterplotReembedColorOption.setDisabled(true); - _removeRowSelection.setDisabled(true); - _speciesExplorerInMapTrigger.setDisabled(true); - _saveGeneTable.setDisabled(true); - _saveSpeciesTable.setDisabled(true); - _usePreComputedTSNE.setDisabled(true); - _applyLogTransformation.setDisabled(true); - _speciesExplorerInMap.setDisabled(true); - _revertRowSelectionChangesToInitial.setDisabled(true); - _toggleScatterplotSelection.setDisabled(true); - _tsnePerplexity.setDisabled(true); - _performGeneTableTsnePerplexity.setDisabled(true); - _performGeneTableTsneKnn.setDisabled(true); - _performGeneTableTsneDistance.setDisabled(true); - _performGeneTableTsneTrigger.setDisabled(true); - _computeTreesToDisplayFromHierarchy.setDisabled(true); - _referenceTreeDataset.setDisabled(true); - _mainPointsDataset.setDisabled(true); - _embeddingDataset.setDisabled(true); - _speciesNamesDataset.setDisabled(true); - _bottomClusterNamesDataset.setDisabled(true); - _middleClusterNamesDataset.setDisabled(true); - _topClusterNamesDataset.setDisabled(true); - _scatterplotEmbeddingPointsUMAPOption.setDisabled(true); - _topHierarchyClusterNamesFrequencyInclusionList.setDisabled(true); - _selectedSpeciesVals.setDisabled(true); - _clusterOrderHierarchy.setDisabled(true); - _rightClickedCluster.setDisabled(true); - _topSelectedHierarchyStatus.setDisabled(true); - _clearRightClickedCluster.setDisabled(true); - _statusColorAction.setDisabled(true); - _searchBox->setDisabled(true); - QApplication::processEvents(); -} - -void SettingsAction::enableDisableButtonsAutomatically() -{ - - bool optionsActionHasOptions = !_speciesExplorerInMap.getOptions().isEmpty(); - bool stringActionHasOptions = !_selectedSpeciesVals.getString().isEmpty(); - - bool bothListsEqual = false; - if (optionsActionHasOptions && stringActionHasOptions) { - QStringList temp = _selectedSpeciesVals.getString().split(" @%$,$%@ "); - QStringList species = _speciesExplorerInMap.getSelectedOptions(); - - std::sort(temp.begin(), temp.end()); - std::sort(species.begin(), species.end()); - bothListsEqual = (temp == species); - } - _revertRowSelectionChangesToInitial.setDisabled(false); - _speciesExplorerInMapTrigger.setDisabled(false); - //if (!stringActionHasOptions) - //{ - // _revertRowSelectionChangesToInitial.setDisabled(true); - //} - //else - //{ - // if (!optionsActionHasOptions) - // { - - // _revertRowSelectionChangesToInitial.setDisabled(false); - // } - // else - // { - // if (bothListsEqual) - // { - - // _revertRowSelectionChangesToInitial.setDisabled(true); - // } - // else - // { - - // _revertRowSelectionChangesToInitial.setDisabled(false); - // } - // } - //} - - - - //if (!optionsActionHasOptions) - //{ - // _speciesExplorerInMapTrigger.setDisabled(true); - - //} - //else - //{ - // _speciesExplorerInMapTrigger.setDisabled(false); - //} - - - -} - - -void SettingsAction::populatePointData(QString& datasetId, std::vector& pointVector, int& numPoints, int& numDimensions, std::vector& dimensionNames) -{ - auto pointDataset = mv::data().getDataset(datasetId); - - if (pointDataset.isValid()) - { - pointDataset->setSelectionIndices({}); - if (pointVector.size() > 0 && numPoints > 0 && numDimensions > 0) { - pointDataset->setData(pointVector.data(), numPoints, numDimensions); - pointDataset->setDimensionNames(dimensionNames); - mv::events().notifyDatasetDataChanged(pointDataset); + result.push_back(leftValue); + ++leftIt; + ++rightIt; } - - } -} -void SettingsAction::populateClusterData(QString& datasetId, std::map>>& clusterMap) -{ - - auto colorDataset = mv::data().getDataset(datasetId); - if (colorDataset.isValid()) - { - colorDataset->getClusters() = QVector(); - for (const auto& pair : clusterMap) - { - QString clusterName = pair.first; - std::pair> value = pair.second; - QColor clusterColor = value.first; - std::vector clusterIndices(value.second.begin(), value.second.end()); - - if (clusterIndices.size() > 0) - { - Cluster clusterValue; - clusterValue.setName(clusterName); - clusterValue.setColor(clusterColor); - clusterValue.setIndices(clusterIndices); - colorDataset->addCluster(clusterValue); - } - } - - mv::events().notifyDatasetDataChanged(colorDataset); } - -} - -void SettingsAction::clearTableSelection(QTableView* tableView) { - if (tableView && tableView->selectionModel()) { - // Clear the current selection - tableView->clearSelection(); - - // Temporarily disable the selection mode to remove highlight - QAbstractItemView::SelectionMode oldMode = tableView->selectionMode(); - tableView->setSelectionMode(QAbstractItemView::NoSelection); - - // Clear the current index - tableView->selectionModel()->setCurrentIndex(QModelIndex(), QItemSelectionModel::NoUpdate); - - // Restore the original selection mode - tableView->setSelectionMode(oldMode); - - // Update the view to ensure changes are reflected - tableView->update(); - } - else { - qDebug() << "TableView or its selection model is null"; - } + return result; } -void SettingsAction::removeSelectionTableRows(QStringList* selectedLeaves) -{ - //check if _selectionDetailsTable is valid - if (_selectionDetailsTable == nullptr) { - return; - } - - clearTableSelection(_selectionDetailsTable); - - QAbstractItemModel* model = _selectionDetailsTable->model(); - - //check if model is valid - if (model == nullptr) { +template +void computeChunkedGeneMeans(const mv::Dataset& pointDataset, const std::vector& allGeneIndices, const PointIndexContainer& pointIndices, Callback&& callback) { + if (!pointDataset.isValid() || allGeneIndices.empty() || pointIndices.empty()) { return; } - //auto colorValues = getSystemModeColor(); - //auto systemColor = colorValues[0]; - //auto valuesColor = colorValues[1]; - - // Iterate through all rows - for (int row = 0; row < model->rowCount(); ++row) { - QModelIndex index = model->index(row, 0); // Assuming species name is in column 0 - QString species = model->data(index, Qt::UserRole).toString(); - - // Check if the species is one of the selected species - if (selectedLeaves->contains(species)) { - for (int col = 0; col < model->columnCount(); ++col) { - QModelIndex cellIndex = model->index(row, col); - _selectionDetailsTable->model()->setData(cellIndex, QBrush(QColor("#00A2ED")), Qt::BackgroundRole); - _selectionDetailsTable->model()->setData(cellIndex, QBrush(QColor("#000000")), Qt::ForegroundRole); - } - } - else - { - //remove existing color from rows - for (int col = 0; col < model->columnCount(); ++col) { - QModelIndex cellIndex = model->index(row, col); - _selectionDetailsTable->model()->setData(cellIndex, QBrush(QColor("#FFFFFF")), Qt::BackgroundRole); - _selectionDetailsTable->model()->setData(cellIndex, QBrush(QColor("#000000")), Qt::ForegroundRole); - } - } - } - -} - -QString SettingsAction::generateTooltip(const ViewPluginSamplerAction::SampleContext& toolTipContext, const QString& clusterDatasetId, bool showTooltip, QString indicesType) { - // Extract and convert GlobalPointIndices and ColorDatasetID from toolTipContext - auto raw_Global_Local_PointIndices = toolTipContext[indicesType].toList(); - - // Convert the list of global point indices to a vector of integers - std::vector global_local_PointIndices; - global_local_PointIndices.reserve(raw_Global_Local_PointIndices.size()); - for (const auto& global_local_PointIndex : raw_Global_Local_PointIndices) { - global_local_PointIndices.push_back(global_local_PointIndex.toInt()); - } - - // If the global point indices list is empty, return an empty result - if (global_local_PointIndices.empty()) { - return {}; - } - - // If there is no cluster dataset ID, return a summary of total points - if (clusterDatasetId.isEmpty()) { - return QString("
Total points: %1
").arg(global_local_PointIndices.size()); - } - - // Retrieve the cluster dataset - auto clusterFullDataset = mv::data().getDataset(clusterDatasetId); - - // If the dataset is invalid, return a summary of total points - if (!clusterFullDataset.isValid()) { - return QString("
Total points: %1
").arg(global_local_PointIndices.size()); - } - - // Get the clusters from the dataset - auto clusterValuesData = clusterFullDataset->getClusters(); - - // If the clusters data is empty, return a summary of total points - if (clusterValuesData.isEmpty()) { - return QString("
Total points: %1
").arg(global_local_PointIndices.size()); - } - - // Process each cluster and find intersections with global point indices - std::map> clusterCountMap; - for (const auto& cluster : clusterValuesData) { - QString clusterName = cluster.getName(); - QColor clusterColor = cluster.getColor(); - auto clusterIndices = cluster.getIndices(); - - // Sort the indices before performing the intersection - std::sort(clusterIndices.begin(), clusterIndices.end()); - std::sort(global_local_PointIndices.begin(), global_local_PointIndices.end()); - - std::vector intersect; - std::set_intersection(clusterIndices.begin(), clusterIndices.end(), - global_local_PointIndices.begin(), global_local_PointIndices.end(), - std::back_inserter(intersect)); - - // If there is an intersection, store the result in the map - if (!intersect.empty()) { - clusterCountMap[clusterName] = std::make_pair(intersect.size(), clusterColor); - } - } - - // If no clusters were found, return a summary of total points - if (clusterCountMap.empty()) { - return QString("
Total points: %1
").arg(global_local_PointIndices.size()); - } - - // Generate HTML output - QString html = ""; - - // Convert the map to a vector of pairs for sorting - std::vector>> clusterVector(clusterCountMap.begin(), clusterCountMap.end()); - - // Sort the vector by count in descending order - std::sort(clusterVector.begin(), clusterVector.end(), [](const auto& a, const auto& b) { - return a.second.first > b.second.first; - }); - - // Find the maximum count for scaling the bars - int maxCount = clusterVector.empty() ? 1 : clusterVector.front().second.first; // Default to 1 if no data. - - // Calculate the maximum width required for text and icon - int maxTextIconWidth = 0; - for (const auto& entry : clusterVector) { - QString clusterName = entry.first; - int textWidth = QFontMetrics(QFont()).horizontalAdvance(clusterName + ": " + QString::number(entry.second.first)); - int iconWidth = 16; // Assuming icon width is 16px - maxTextIconWidth = std::max(maxTextIconWidth, textWidth + iconWidth); - } - maxTextIconWidth = maxTextIconWidth + 2; - // Populate the divs with cluster data - html += "
"; - for (const auto& entry : clusterVector) { - QString clusterName = entry.first; - int count = entry.second.first; - QString colorHex = "#a6a6a6"; // entry.second.second.name(); - QColor color(entry.second.second); - - QString textColor = "black"; - int barWidth = (maxCount > 0) ? static_cast((static_cast(count) / maxCount) * 100) : 0; - barWidth = std::max(barWidth, 5); // Minimum width for visibility - - QString iconPath = ":/speciesicons/SpeciesIcons/" + clusterName + ".svg"; - QString iconHtml; - if (QFile::exists(iconPath)) { - QFile file(iconPath); - if (file.open(QIODevice::ReadOnly)) { - QByteArray iconData = file.readAll().toBase64(); - iconHtml = QString(" ").arg(QString(iconData)); - } else { - iconHtml = "
"; // Placeholder - } - } else { - iconHtml = "
"; // Placeholder - } - - html += "
"; - html += "
"; - html += "
" - + iconHtml + clusterName + ": " + QString::number(count) + "
"; - html += "
"; - } - - html += "
"; - - html += ""; + // Large selections are faster when we fetch a block of genes once and + // aggregate the point-major buffer locally instead of calling into the + // dataset backend once per gene. + std::vector geneChunkIndices; + geneChunkIndices.reserve(std::min(kGeneChunkSize, static_cast(allGeneIndices.size()))); + std::vector chunkData; + std::vector chunkMeans; - return html; -} - + const int pointCount = static_cast(pointIndices.size()); -void SettingsAction::updateSelectedSpeciesCounts(QJsonObject& node, const std::map& speciesCountMap) { - // Check if the "name" key exists in the current node - if (node.contains("name")) { - QString nodeName = node["name"].toString(); - auto it = speciesCountMap.find(nodeName); - // If the "name" is found in the speciesExpressionMap, update "mean" if it exists or add "mean" if it doesn't exist - if (it != speciesCountMap.end()) { - node["cellCounts"] = it->second; // Use it->second to access the value in the map - } - } + for (int start = 0; start < static_cast(allGeneIndices.size()); start += kGeneChunkSize) { + const int chunkSize = std::min(kGeneChunkSize, static_cast(allGeneIndices.size()) - start); - // If the node has "children", recursively update them as well - if (node.contains("children")) { - QJsonArray children = node["children"].toArray(); - for (int i = 0; i < children.size(); ++i) { - QJsonObject child = children[i].toObject(); - updateSelectedSpeciesCounts(child, speciesCountMap); // Recursive call - children[i] = child; // Update the modified object back into the array - } - node["children"] = children; // Update the modified array back into the parent JSON object - } -} -/* -QString SettingsAction::createJsonTreeFromNewick(QString tree, std::vector leafnames, std::map speciesMeanValues) -{ - int i = 0; - std::string jsonString = ""; - std::stringstream jsonStream; - std::string newick = tree.toStdString(); - while (i < newick.size()) { - if (newick[i] == '(') { - jsonStream << "{\n\"children\": ["; - i++; - } - else if (newick[i] == ',') { - jsonStream << ","; - i++; - } - else if (newick[i] == ')') { - jsonStream << "],\n\"id\": 1,\n\"score\": 1,\n\"branchLength\": 1.0,\n\"width\": 1\n}"; - i++; - } - else if (newick[i] == ';') { - break; - } - else { - if (isdigit(newick[i])) { - int skip = 1; - std::string num = ""; - for (int j = i; j < newick.size(); j++) { - if (isdigit(newick[j])) { - continue; - } - else { - num = newick.substr(i, j - i); + geneChunkIndices.assign(allGeneIndices.begin() + start, allGeneIndices.begin() + start + chunkSize); + chunkData.assign(static_cast(pointCount) * static_cast(chunkSize), 0.0f); + chunkMeans.assign(chunkSize, 0.0f); - skip = j - i; - break; - } - } - std::string species = leafnames[(std::stoi(num) - 1)].toStdString(); - //std::string meanValue = std::to_string(speciesMeanValues[QString::fromStdString(species)]); - auto it = speciesMeanValues.find(QString::fromStdString(species)); - std::string meanValue; - if (it != speciesMeanValues.end()) { - // Key found, use the corresponding value - meanValue = std::to_string(it->second.meanSelected); - } - else { - // Key not found, assign -1 - meanValue = "-1"; - } + pointDataset->populateDataForDimensions(chunkData, geneChunkIndices, pointIndices); - jsonStream << "{\n\"color\": \"#000000\",\n\"hastrait\": true,\n\"iscollapsed\": false,\n\"branchLength\": 1.0,\n\"cellCounts\": " << 0 << ",\n\"mean\": "<< meanValue <<", \n\"name\": \"" << species << "\"\n}"; - i += skip; + for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) { + const int rowOffset = pointIndex * chunkSize; + for (int geneOffset = 0; geneOffset < chunkSize; ++geneOffset) { + chunkMeans[geneOffset] += chunkData[rowOffset + geneOffset]; } } - } - - jsonString = jsonStream.str(); - - nlohmann::json json = nlohmann::json::parse(jsonString); - std::string jsonStr = json.dump(4); - //qDebug()<< "CrossSpeciesComparisonClusterRankPlugin::createJsonTree: jsonStr: " << QString::fromStdString(jsonStr); - QString formattedTree = QString::fromStdString(jsonStr); - - - return formattedTree; -} -*/ -/* -std::string SettingsAction::mergeToNewick(int* merge, int numOfLeaves) { - std::vector labels(numOfLeaves); - for (int i = 0; i < numOfLeaves; ++i) { - labels[i] = std::to_string(i + 1); - } - - std::stack stack; - - for (int i = 0; i < 2 * (numOfLeaves - 1); i += 2) { - int left = merge[i]; - int right = merge[i + 1]; - - std::string leftStr; - if (left < 0) { - leftStr = labels[-left - 1]; - } - else { - leftStr = stack.top(); - stack.pop(); - } - std::string rightStr; - if (right < 0) { - rightStr = labels[-right - 1]; + const float inversePointCount = 1.0f / static_cast(pointCount); + for (float& meanValue : chunkMeans) { + meanValue *= inversePointCount; } - else { - rightStr = stack.top(); - stack.pop(); - } - - std::string merged = "(" + leftStr + "," + rightStr + ")"; - stack.push(merged); - } - return stack.top() + ";"; -} -*/ -double* SettingsAction::condensedDistanceMatrix(const std::vector& items) { - size_t n = items.size(); - double* distmat = new double[(n * (n - 1)) / 2]; - size_t k = 0; - -#pragma omp parallel for schedule(dynamic) collapse(2) private(k) - for (size_t i = 0; i < n; ++i) { - for (size_t j = i + 1; j < n; ++j) { - k = ((n * (n - 1)) / 2) - ((n - i) * (n - i - 1)) / 2 + j - i - 1; - distmat[k] = std::abs(items[i] - items[j]); - } + callback(start, geneChunkIndices, chunkMeans); } - - return distmat; } -SettingsAction::Widget::Widget(QWidget* parent, SettingsAction* SettingsAction) : - WidgetActionWidget(parent, SettingsAction) -{ } - -SettingsAction::OptionSelectionAction::Widget::Widget(QWidget* parent, OptionSelectionAction* optionSelectionAction) : - WidgetActionWidget(parent, optionSelectionAction) -{ } - -inline SettingsAction::OptionSelectionAction::OptionSelectionAction(SettingsAction& SettingsAction) : - GroupAction(nullptr, "CrossSpeciesComparisonGeneDetectPluginOptionSelectionAction"), - _settingsAction(SettingsAction) -{ - setText("Options"); - setIcon(mv::util::StyledIcon("wrench")); - //addAction(&_settingsAction.getTableModelAction()); - //addAction(&_settingsAction.getSelectedGeneAction()); - //addAction(&_settingsAction.getSelectedRowIndexAction()); - //addAction(&_settingsAction.getFilteringTreeDatasetAction()); - //addAction(&_settingsAction.getOptionSelectionAction()); - //addAction(&_settingsAction.getStartComputationTriggerAction()); - //addAction(&_settingsAction.getReferenceTreeDatasetAction()); - //addAction(&_settingsAction.getMainPointsDataset()); - //addAction(&_settingsAction.getHierarchyTopClusterDataset()); - //addAction(&_settingsAction.getHierarchyMiddleClusterDataset()); - //addAction(&_settingsAction.getHierarchyBottomClusterDataset()); - //addAction(&_settingsAction.getSpeciesNamesDataset()); - //addAction(&_settingsAction.getSelectedClusterNames()); - - -} +} // namespace +// Split implementation by responsibility to keep navigation manageable while +// preserving the original SettingsAction behavior and method signatures. -void SettingsAction::fromVariantMap(const QVariantMap& variantMap) -{ - WidgetAction::fromVariantMap(variantMap); +#include "SettingsAction.Ui.inl" - _geneNamesConnection.fromParentVariantMap(variantMap); - _createRowMultiSelectTree.fromParentVariantMap(variantMap); - _listModel.fromParentVariantMap(variantMap); - _selectedGene.fromParentVariantMap(variantMap); - _mainPointsDataset.fromParentVariantMap(variantMap); - _embeddingDataset.fromParentVariantMap(variantMap); - _speciesNamesDataset.fromParentVariantMap(variantMap); - _bottomClusterNamesDataset.fromParentVariantMap(variantMap); - _middleClusterNamesDataset.fromParentVariantMap(variantMap); - _topClusterNamesDataset.fromParentVariantMap(variantMap); - _filteredGeneNamesVariant.fromParentVariantMap(variantMap); - _topNGenesFilter.fromParentVariantMap(variantMap); - _filteringEditTreeDataset.fromParentVariantMap(variantMap); - _referenceTreeDataset.fromParentVariantMap(variantMap); - _selectedRowIndex.fromParentVariantMap(variantMap); - _performGeneTableTsneAction.fromParentVariantMap(variantMap); - _tsnePerplexity.fromParentVariantMap(variantMap); - _performGeneTableTsnePerplexity.fromParentVariantMap(variantMap); - _performGeneTableTsneKnn.fromParentVariantMap(variantMap); - _performGeneTableTsneDistance.fromParentVariantMap(variantMap); - _performGeneTableTsneTrigger.fromParentVariantMap(variantMap); - _hiddenShowncolumns.fromParentVariantMap(variantMap); - _speciesExplorerInMap.fromParentVariantMap(variantMap); - _topHierarchyClusterNamesFrequencyInclusionList.fromParentVariantMap(variantMap); - _scatterplotReembedColorOption.fromParentVariantMap(variantMap); - _scatterplotEmbeddingPointsUMAPOption.fromParentVariantMap(variantMap); - _selectedSpeciesVals.fromParentVariantMap(variantMap); - _clusterOrderHierarchy.fromParentVariantMap(variantMap); - _rightClickedCluster.fromParentVariantMap(variantMap); - _topSelectedHierarchyStatus.fromParentVariantMap(variantMap); - _clearRightClickedCluster.fromParentVariantMap(variantMap); - _removeRowSelection.fromParentVariantMap(variantMap); - _revertRowSelectionChangesToInitial.fromParentVariantMap(variantMap); - _speciesExplorerInMapTrigger.fromParentVariantMap(variantMap); - _statusColorAction.fromParentVariantMap(variantMap); - _typeofTopNGenes.fromParentVariantMap(variantMap); - _clusterCountSortingType.fromParentVariantMap(variantMap); - _usePreComputedTSNE.fromParentVariantMap(variantMap); - _applyLogTransformation.fromParentVariantMap(variantMap); +#include "SettingsAction.Analysis.inl" -} +#include "SettingsAction.Data.inl" -QVariantMap SettingsAction::toVariantMap() const -{ - QVariantMap variantMap = WidgetAction::toVariantMap(); +#include "SettingsAction.Tree.inl" - _geneNamesConnection.insertIntoVariantMap(variantMap); - _createRowMultiSelectTree.insertIntoVariantMap(variantMap); - _listModel.insertIntoVariantMap(variantMap); - _selectedGene.insertIntoVariantMap(variantMap); - _mainPointsDataset.insertIntoVariantMap(variantMap); - _embeddingDataset.insertIntoVariantMap(variantMap); - _speciesNamesDataset.insertIntoVariantMap(variantMap); - _bottomClusterNamesDataset.insertIntoVariantMap(variantMap); - _middleClusterNamesDataset.insertIntoVariantMap(variantMap); - _topClusterNamesDataset.insertIntoVariantMap(variantMap); - _filteredGeneNamesVariant.insertIntoVariantMap(variantMap); - _topNGenesFilter.insertIntoVariantMap(variantMap); - _filteringEditTreeDataset.insertIntoVariantMap(variantMap); - _referenceTreeDataset.insertIntoVariantMap(variantMap); - _selectedRowIndex.insertIntoVariantMap(variantMap); - _performGeneTableTsneAction.insertIntoVariantMap(variantMap); - _tsnePerplexity.insertIntoVariantMap(variantMap); - _performGeneTableTsnePerplexity.insertIntoVariantMap(variantMap); - _performGeneTableTsneDistance.insertIntoVariantMap(variantMap); - _performGeneTableTsneKnn.insertIntoVariantMap(variantMap); - _performGeneTableTsneTrigger.insertIntoVariantMap(variantMap); - _hiddenShowncolumns.insertIntoVariantMap(variantMap); - _speciesExplorerInMap.insertIntoVariantMap(variantMap); - _topHierarchyClusterNamesFrequencyInclusionList.insertIntoVariantMap(variantMap); - _scatterplotReembedColorOption.insertIntoVariantMap(variantMap); - _scatterplotEmbeddingPointsUMAPOption.insertIntoVariantMap(variantMap); - _selectedSpeciesVals.insertIntoVariantMap(variantMap); - _clusterOrderHierarchy.insertIntoVariantMap(variantMap); - _rightClickedCluster.insertIntoVariantMap(variantMap); - _topSelectedHierarchyStatus.insertIntoVariantMap(variantMap); - _clearRightClickedCluster.insertIntoVariantMap(variantMap); - _removeRowSelection.insertIntoVariantMap(variantMap); - _revertRowSelectionChangesToInitial.insertIntoVariantMap(variantMap); - _speciesExplorerInMapTrigger.insertIntoVariantMap(variantMap); - _statusColorAction.insertIntoVariantMap(variantMap); - _typeofTopNGenes.insertIntoVariantMap(variantMap); - _clusterCountSortingType.insertIntoVariantMap(variantMap); - _usePreComputedTSNE.insertIntoVariantMap(variantMap); - _applyLogTransformation.insertIntoVariantMap(variantMap); - return variantMap; -} +#include "SettingsAction.Serialization.inl" From 90df039cf2158e4ed15dadfac68d4dd75001b9d2 Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu <44787782+basusoumyadeep@users.noreply.github.com> Date: Sat, 25 Apr 2026 14:22:26 +0200 Subject: [PATCH 02/10] add license --- LICENSE.txt | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 6 +- 2 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..4b7725f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,167 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md index b2c7302..396d0d4 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,4 @@ If you extend the analysis logic, prefer: - batched point reads over per-gene calls, - explicit invalidation/caching boundaries, - stable table widths over repeated `resizeColumnsToContents()` calls in hot paths, -- and avoiding repeated signal connections inside methods that run on every update. - -## License / Ownership - -This repository currently does not declare a license in the root. Add one if the project is intended for redistribution or external collaboration. +- and avoiding repeated signal connections inside methods that run on every update. \ No newline at end of file From 687d93035739fd17f4e95ceac79bf49fe7783de9 Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu <44787782+basusoumyadeep@users.noreply.github.com> Date: Sat, 25 Apr 2026 14:30:43 +0200 Subject: [PATCH 03/10] remove debug message[skip ci] --- src/SettingsAction.Data.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SettingsAction.Data.inl b/src/SettingsAction.Data.inl index a18b46b..0e9435f 100644 --- a/src/SettingsAction.Data.inl +++ b/src/SettingsAction.Data.inl @@ -79,8 +79,8 @@ void SettingsAction::removeDatasets(int groupId) for (const auto& id : ids) { auto ds = idToDataset.value(id); if (ds.isValid()) { - qDebug() << "Deleting:" << ds->getId() << "with name:" << ds->getGuiName() - << "depth:" << depthCache[id]; + //qDebug() << "Deleting:" << ds->getId() << "with name:" << ds->getGuiName() + //<< "depth:" << depthCache[id]; mv::data().removeDataset(ds); } From b9a8c8c21a1a2338a82745fc3c13fc7405d4cd3d Mon Sep 17 00:00:00 2001 From: sbasu Date: Fri, 19 Jun 2026 15:21:44 +0200 Subject: [PATCH 04/10] Add using namespace mv --- src/CrossSpeciesComparisonGeneDetectPlugin.h | 6 +- src/SettingsAction.h | 60 ++++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/CrossSpeciesComparisonGeneDetectPlugin.h b/src/CrossSpeciesComparisonGeneDetectPlugin.h index 92e9739..d6e29ed 100644 --- a/src/CrossSpeciesComparisonGeneDetectPlugin.h +++ b/src/CrossSpeciesComparisonGeneDetectPlugin.h @@ -77,9 +77,9 @@ class CrossSpeciesComparisonGeneDetectPlugin : public ViewPlugin SettingsAction _settingsAction; //HorizontalToolbarAction _toolbarAction; // Toolbar action that is shown in the main window //VerticalToolbarAction _verticalGroupAction; // Vertical group action that is shown in the main window - Dataset _pointsDataset; - Dataset _clusterDataset; - Dataset _lowDimTSNEDataset; + mv::Dataset _pointsDataset; + mv::Dataset _clusterDataset; + mv::Dataset _lowDimTSNEDataset; }; diff --git a/src/SettingsAction.h b/src/SettingsAction.h index e00298b..77ad985 100644 --- a/src/SettingsAction.h +++ b/src/SettingsAction.h @@ -277,32 +277,32 @@ class SettingsAction : public WidgetAction StringAction& getTopSelectedHierarchyStatus() { return _topSelectedHierarchyStatus; } TriggerAction& getClearRightClickedCluster() { return _clearRightClickedCluster; } - Dataset& getSelectedPointsTSNEDatasetForGeneTable() { return _selectedPointsTSNEDatasetForGeneTable; } + mv::Dataset& getSelectedPointsTSNEDatasetForGeneTable() { return _selectedPointsTSNEDatasetForGeneTable; } QStringList getCurrentHierarchyItemsMiddleForTable() { return _currentHierarchyItemsMiddleForTable; } //IntegralAction& setPerformGeneTableTsnePerplexity() { return _performGeneTableTsnePerplexity; } //tsne relatedDatasets /* - Dataset _selectedPointsTSNEDataset; - Dataset _selectedPointsDataset; - Dataset _selectedPointsEmbeddingDataset; + mv::Dataset _selectedPointsTSNEDataset; + mv::Dataset _selectedPointsDataset; + mv::Dataset _selectedPointsEmbeddingDataset; - Dataset _tsneDatasetSpeciesColors; - Dataset _tsneDatasetClusterColors; - Dataset _tsneDatasetExpressionColors; + mv::Dataset _tsneDatasetSpeciesColors; + mv::Dataset _tsneDatasetClusterColors; + mv::Dataset _tsneDatasetExpressionColors; */ - Dataset& getSelectedPointsTSNEDataset() { return _selectedPointsTSNEDataset; } - Dataset& getSelectedPointsDataset() { return _selectedPointsDataset; } - Dataset& getSelectedPointsEmbeddingDataset() { return _selectedPointsEmbeddingDataset; } + mv::Dataset& getSelectedPointsTSNEDataset() { return _selectedPointsTSNEDataset; } + mv::Dataset& getSelectedPointsDataset() { return _selectedPointsDataset; } + mv::Dataset& getSelectedPointsEmbeddingDataset() { return _selectedPointsEmbeddingDataset; } - Dataset& getTsneDatasetSpeciesColors() { return _tsneDatasetSpeciesColors; } - Dataset& getTsneDatasetClusterColors() { return _tsneDatasetClusterColors; } - Dataset& getTsneDatasetExpressionColors() { return _tsneDatasetExpressionColors; } + mv::Dataset& getTsneDatasetSpeciesColors() { return _tsneDatasetSpeciesColors; } + mv::Dataset& getTsneDatasetClusterColors() { return _tsneDatasetClusterColors; } + mv::Dataset& getTsneDatasetExpressionColors() { return _tsneDatasetExpressionColors; } std::vector& getSelectedIndicesFromStorage() { return _selectedIndicesFromStorage; } - Dataset & getFilteredUMAPDatasetPoints() { return _filteredUMAPDatasetPoints; } - Dataset & getFilteredUMAPDatasetColors() { return _filteredUMAPDatasetColors; } - Dataset & getFilteredUMAPDatasetClusters() { return _filteredUMAPDatasetClusters; } + mv::Dataset & getFilteredUMAPDatasetPoints() { return _filteredUMAPDatasetPoints; } + mv::Dataset & getFilteredUMAPDatasetColors() { return _filteredUMAPDatasetColors; } + mv::Dataset & getFilteredUMAPDatasetClusters() { return _filteredUMAPDatasetClusters; } QStatusBar* getStatusBarActionWidget() const { return _statusBarActionWidget; } QMessageBox* getPopupMessageInit() const { return _popupMessageInit; } QMessageBox* getPopupMessageTreeCreationCompletion() const { return _popupMessageTreeCreationCompletion; } @@ -323,9 +323,9 @@ class SettingsAction : public WidgetAction QSet& getUniqueReturnGeneList() { return _uniqueReturnGeneList; } std::vector& getTotalGeneList() { return _totalGeneList; } - Dataset& getGeneSimilarityPoints() { return _geneSimilarityPoints; } + mv::Dataset& getGeneSimilarityPoints() { return _geneSimilarityPoints; } //std::vector& getGeneSimilarityClusters() { return _geneSimilarityClusters; } - Dataset& getGeneSimilarityClusterColoring() { return _geneSimilarityClusterColoring; } + mv::Dataset& getGeneSimilarityClusterColoring() { return _geneSimilarityClusterColoring; } std::vector& getGeneOrder() { return _geneOrder; } bool& getPauseStatusUpdates() { return _pauseStatusUpdates; } bool& setPauseStatusUpdates(bool flag) { return _pauseStatusUpdates = flag; } @@ -407,20 +407,20 @@ class SettingsAction : public WidgetAction StringAction _selectedSpeciesVals; OptionAction _typeofTopNGenes; - Dataset _selectedPointsTSNEDataset; - Dataset _selectedPointsDataset; - Dataset _selectedPointsEmbeddingDataset; - Dataset _filteredUMAPDatasetPoints; - Dataset _filteredUMAPDatasetColors; - Dataset _filteredUMAPDatasetClusters; + mv::Dataset _selectedPointsTSNEDataset; + mv::Dataset _selectedPointsDataset; + mv::Dataset _selectedPointsEmbeddingDataset; + mv::Dataset _filteredUMAPDatasetPoints; + mv::Dataset _filteredUMAPDatasetColors; + mv::Dataset _filteredUMAPDatasetClusters; - Dataset _tsneDatasetSpeciesColors; - Dataset _tsneDatasetClusterColors; - Dataset _tsneDatasetExpressionColors; + mv::Dataset _tsneDatasetSpeciesColors; + mv::Dataset _tsneDatasetClusterColors; + mv::Dataset _tsneDatasetExpressionColors; - Dataset _geneSimilarityPoints; + mv::Dataset _geneSimilarityPoints; //std::vector _geneSimilarityClusters; - Dataset _geneSimilarityClusterColoring; + mv::Dataset _geneSimilarityClusterColoring; TriggerAction _removeRowSelection; TriggerAction _revertRowSelectionChangesToInitial; @@ -459,7 +459,7 @@ class SettingsAction : public WidgetAction OptionAction _performGeneTableTsneDistance; TriggerAction _performGeneTableTsneTrigger; TriggerAction _computeTreesToDisplayFromHierarchy; - Dataset _selectedPointsTSNEDatasetForGeneTable; + mv::Dataset _selectedPointsTSNEDatasetForGeneTable; bool _pauseStatusUpdates=false; QStringList _deleteDatasetIds; std::vector _geneOrder; From 66f34c0a7f0aae0bc3e48aadbf6c8e62299ad959 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 1 Jul 2026 09:05:46 +0200 Subject: [PATCH 05/10] Adhere to updated core serialization and parallelization API (#128) * Include util::Serialization in plugin Add #include to CrossSpeciesComparisonGeneDetectPlugin.cpp so the plugin can use serialization utilities. This ensures serialization types/functions are available where needed and tidies up the include ordering. * Qualify Dataset types with mv:: namespace Prefix Dataset, Points and Clusters types with the mv:: namespace in headers to remove ambiguity and ensure correct type resolution. Updated member variables and getter return types in src/CrossSpeciesComparisonGeneDetectPlugin.h and src/SettingsAction.h. No behavioral changes intended, only type qualification. --- src/CrossSpeciesComparisonGeneDetectPlugin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CrossSpeciesComparisonGeneDetectPlugin.cpp b/src/CrossSpeciesComparisonGeneDetectPlugin.cpp index 71f6ebc..13b14c4 100644 --- a/src/CrossSpeciesComparisonGeneDetectPlugin.cpp +++ b/src/CrossSpeciesComparisonGeneDetectPlugin.cpp @@ -1,5 +1,7 @@ #include "CrossSpeciesComparisonGeneDetectPlugin.h" +#include + #include #include #include From 503e91e738a712edf04d327f2a527e27ba7eafae Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu <44787782+sbvis@users.noreply.github.com> Date: Mon, 13 Jul 2026 19:56:42 +0200 Subject: [PATCH 06/10] Update SettingsAction.h --- src/SettingsAction.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SettingsAction.h b/src/SettingsAction.h index 77ad985..bd98867 100644 --- a/src/SettingsAction.h +++ b/src/SettingsAction.h @@ -39,6 +39,7 @@ #include #include #include +#include #include using namespace mv::gui; From 88316d6967b99788da0f6a97754c1f6c71dd4fd9 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 14 Jul 2026 09:21:18 +0200 Subject: [PATCH 07/10] Fix includes --- src/SettingsAction.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SettingsAction.h b/src/SettingsAction.h index 77ad985..bd98867 100644 --- a/src/SettingsAction.h +++ b/src/SettingsAction.h @@ -39,6 +39,7 @@ #include #include #include +#include #include using namespace mv::gui; From d83f23da2d8311f988e531c111c13e339db6ae40 Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu Date: Tue, 21 Jul 2026 17:27:14 +0200 Subject: [PATCH 08/10] change repo name --- CMakeLists.txt | 24 +++---- README.md | 34 ++++----- conanfile.py | 18 ++--- ...tect_chart.qrc => XSCGeneDetect_chart.qrc} | 0 src/SettingsAction.Analysis.inl | 2 +- src/SettingsAction.Serialization.inl | 2 +- src/SettingsAction.Tree.inl | 2 +- src/SettingsAction.Ui.inl | 16 ++--- src/SettingsAction.cpp | 4 +- src/SettingsAction.h | 6 +- ...tectPlugin.cpp => XSCGeneDetectPlugin.cpp} | 69 +++++++++---------- ...neDetectPlugin.h => XSCGeneDetectPlugin.h} | 18 ++--- ...ctPlugin.json => XSCGeneDetectPlugin.json} | 2 +- 13 files changed, 95 insertions(+), 102 deletions(-) rename res/{CrossSpeciesComparisonGeneDetect_chart.qrc => XSCGeneDetect_chart.qrc} (100%) rename src/{CrossSpeciesComparisonGeneDetectPlugin.cpp => XSCGeneDetectPlugin.cpp} (97%) rename src/{CrossSpeciesComparisonGeneDetectPlugin.h => XSCGeneDetectPlugin.h} (84%) rename src/{CrossSpeciesComparisonGeneDetectPlugin.json => XSCGeneDetectPlugin.json} (54%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1959e6..efc6c10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.22) # ----------------------------------------------------------------------------- -# CrossSpeciesComparisonGeneDetectPlugin +# XSCGeneDetectPlugin # ----------------------------------------------------------------------------- -PROJECT("CrossSpeciesComparisonGeneDetectPlugin") +PROJECT("XSCGeneDetectPlugin") # ----------------------------------------------------------------------------- # CMake Options @@ -36,8 +36,8 @@ find_package(ManiVault COMPONENTS Core PointData ClusterData CONFIG QUIET) # ----------------------------------------------------------------------------- # Define the plugin sources set(PLUGIN_SOURCES - src/CrossSpeciesComparisonGeneDetectPlugin.h - src/CrossSpeciesComparisonGeneDetectPlugin.cpp + src/XSCGeneDetectPlugin.h + src/XSCGeneDetectPlugin.cpp src/SettingsAction.h src/SettingsAction.cpp src/SettingsAction.Ui.inl @@ -45,7 +45,7 @@ set(PLUGIN_SOURCES src/SettingsAction.Data.inl src/SettingsAction.Tree.inl src/SettingsAction.Serialization.inl - src/CrossSpeciesComparisonGeneDetectPlugin.json + src/XSCGeneDetectPlugin.json ) set(LIBS src/lib/Clustering/fastcluster.cpp @@ -61,13 +61,13 @@ set(LIBS ) set(PLUGIN_MOC_HEADERS - src/CrossSpeciesComparisonGeneDetectPlugin.h + src/XSCGeneDetectPlugin.h ) set(AUX - res/CrossSpeciesComparisonGeneDetect_chart.qrc + res/XSCGeneDetect_chart.qrc ) -qt6_add_resources(RESOURCE_FILES res/CrossSpeciesComparisonGeneDetect_chart.qrc) +qt6_add_resources(RESOURCE_FILES res/XSCGeneDetect_chart.qrc) source_group(Plugin FILES ${PLUGIN_SOURCES}) source_group(LibFiles FILES ${LIBS}) @@ -107,11 +107,11 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Concurrent ) # Allow override of plugin library path from the build system -set(CROSSSPECIESCOMPARISONTREEDATA_LINK_LIBRARY "${CROSSSPECIESCOMPARISONTREEDATA_LINK_LIBRARY}" CACHE STRING "Path to CrossSpeciesComparisonTreeData library") -if(NOT CROSSSPECIESCOMPARISONTREEDATA_LINK_LIBRARY) +set(XSCTREEDATA_LINK_LIBRARY "${XSCTREEDATA_LINK_LIBRARY}" CACHE STRING "Path to XSCTreeData library") +if(NOT XSCTREEDATA_LINK_LIBRARY) set(CSCTDPLUGIN_LINK_PATH "${MV_CSCTD_INSTALL_DIR}/$/$,lib,Plugins>") set(MV_LINK_SUFFIX $,${CMAKE_LINK_LIBRARY_SUFFIX},${CMAKE_SHARED_LIBRARY_SUFFIX}>) - set(CROSSSPECIESCOMPARISONTREEDATA_LINK_LIBRARY "${CSCTDPLUGIN_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}CrossSpeciesComparisonTreeData${MV_LINK_SUFFIX}") + set(XSCTREEDATA_LINK_LIBRARY "${CSCTDPLUGIN_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}XSCTreeData${MV_LINK_SUFFIX}") endif() # Link to ManiVault and data plugins @@ -119,7 +119,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ManiVault::Core) target_link_libraries(${PROJECT_NAME} PRIVATE ManiVault::PointData) target_link_libraries(${PROJECT_NAME} PRIVATE ManiVault::ClusterData) -target_link_libraries(${PROJECT_NAME} PRIVATE "${CROSSSPECIESCOMPARISONTREEDATA_LINK_LIBRARY}") +target_link_libraries(${PROJECT_NAME} PRIVATE "${XSCTREEDATA_LINK_LIBRARY}") # ----------------------------------------------------------------------------- # Target installation diff --git a/README.md b/README.md index 396d0d4..dd696ea 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# CrossSpeciesComparisonGeneDetectPlugin +# XSCGeneDetectPlugin -`CrossSpeciesComparisonGeneDetectPlugin` is a ManiVault view plugin for exploring cross-species gene expression differences from point-based single-cell style datasets. +`XSCGeneDetectPlugin` is a ManiVault view plugin for exploring cross-species gene expression differences from point-based single-cell style datasets. The plugin is designed for workflows where a user: @@ -18,7 +18,7 @@ At a high level, the plugin combines five pieces of information: - a low-dimensional embedding dataset (`Points`), - a species assignment dataset (`Cluster`), - one or more hierarchy / cell-type cluster datasets (`Cluster`), -- and a reference phylogenetic tree dataset (`CrossSpeciesComparisonTree`). +- and a reference phylogenetic tree dataset (`XSCTree`). From these inputs, it computes: @@ -32,16 +32,16 @@ From these inputs, it computes: ## Repository Layout -- [CMakeLists.txt](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/CMakeLists.txt): CMake target definition and ManiVault/Qt integration. -- [src/CrossSpeciesComparisonGeneDetectPlugin.h](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/CrossSpeciesComparisonGeneDetectPlugin.h): plugin class declaration. -- [src/CrossSpeciesComparisonGeneDetectPlugin.cpp](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/CrossSpeciesComparisonGeneDetectPlugin.cpp): plugin wiring, view integration, table rendering, and scatterplot/tree coordination. -- [src/SettingsAction.h](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.h): main state container and action declarations. -- [src/SettingsAction.cpp](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.cpp): shared helpers and compilation unit for the split inline implementation. -- [src/SettingsAction.Ui.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Ui.inl): UI/action wiring. -- [src/SettingsAction.Analysis.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Analysis.inl): main analysis and ranking logic. -- [src/SettingsAction.Data.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Data.inl): dataset creation, model generation, and export helpers. -- [src/SettingsAction.Tree.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Tree.inl): tree-related logic. -- [src/SettingsAction.Serialization.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/src/SettingsAction.Serialization.inl): serialization support. +- [CMakeLists.txt](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/CMakeLists.txt): CMake target definition and ManiVault/Qt integration. +- [src/XSCGeneDetectPlugin.h](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/XSCGeneDetectPlugin.h): plugin class declaration. +- [src/XSCGeneDetectPlugin.cpp](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/XSCGeneDetectPlugin.cpp): plugin wiring, view integration, table rendering, and scatterplot/tree coordination. +- [src/SettingsAction.h](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/SettingsAction.h): main state container and action declarations. +- [src/SettingsAction.cpp](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/SettingsAction.cpp): shared helpers and compilation unit for the split inline implementation. +- [src/SettingsAction.Ui.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/SettingsAction.Ui.inl): UI/action wiring. +- [src/SettingsAction.Analysis.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/SettingsAction.Analysis.inl): main analysis and ranking logic. +- [src/SettingsAction.Data.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/SettingsAction.Data.inl): dataset creation, model generation, and export helpers. +- [src/SettingsAction.Tree.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/SettingsAction.Tree.inl): tree-related logic. +- [src/SettingsAction.Serialization.inl](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/src/SettingsAction.Serialization.inl): serialization support. ## Build Requirements @@ -50,13 +50,13 @@ The project expects a ManiVault development environment with: - CMake 3.22 or newer, - Qt 6 with `Widgets`, `WebEngineWidgets`, and `Concurrent`, - ManiVault packages for `Core`, `PointData`, and `ClusterData`, -- the `CrossSpeciesComparisonTreeData` plugin/library available at build or install time. +- the `XSCTreeData` plugin/library available at build or install time. Relevant CMake variables: - `ManiVault_INSTALL_DIR`: ManiVault installation root. -- `MV_CSCTD_INSTALL_DIR`: installation root for `CrossSpeciesComparisonTreeData`. -- `CROSSSPECIESCOMPARISONTREEDATA_LINK_LIBRARY`: optional explicit override for the tree data plugin library. +- `MV_CSCTD_INSTALL_DIR`: installation root for `XSCTreeData`. +- `XSCTREEDATA_LINK_LIBRARY`: optional explicit override for the tree data plugin library. ## Building @@ -67,7 +67,7 @@ cmake -S . -B build -DManiVault_INSTALL_DIR="C:\Path\To\ManiVault" cmake --build build --config Release ``` -On successful build, the plugin is installed into the ManiVault `Plugins` directory via the post-build install step defined in [CMakeLists.txt](E:/Coding/DevBundle/GenerateTHESISImages/source/CrossSpeciesComparisonGeneDetectPlugin/CMakeLists.txt). +On successful build, the plugin is installed into the ManiVault `Plugins` directory via the post-build install step defined in [CMakeLists.txt](E:/Coding/DevBundle/GenerateTHESISImages/source/XSCGeneDetectPlugin/CMakeLists.txt). ## Runtime Data Expectations diff --git a/conanfile.py b/conanfile.py index d5b5584..97bc147 100644 --- a/conanfile.py +++ b/conanfile.py @@ -8,7 +8,7 @@ from conans import tools import shutil -class CrossSpeciesComparisonGeneDetectPluginConan(ConanFile): +class XSCGeneDetectPluginConan(ConanFile): """Class to package using conan Packages both RELEASE and RELWITHDEBINFO. @@ -17,10 +17,10 @@ class CrossSpeciesComparisonGeneDetectPluginConan(ConanFile): as described in https://github.com/ManiVaultStudio/core/wiki/Branch-naming-rules """ - name = "CrossSpeciesComparisonGeneDetectPlugin" - description = """Viewer of cell CrossSpeciesComparisonTreeData data as described in a .swc file.""" - topics = ("manivault", "plugin", "view", "CrossSpeciesComparisonGeneDetectPlugin") - url = "https://github.com/ManiVaultStudio/CrossSpeciesComparisonGeneDetectPlugin" + name = "XSCGeneDetectPlugin" + description = """Viewer of cell XSCTreeData data as described in a .swc file.""" + topics = ("manivault", "plugin", "view", "XSCGeneDetectPlugin") + url = "https://github.com/ManiVaultStudio/XSCGeneDetectPlugin" author = "julianthijssen@gmail.com" # conan recipe author license = "LGPL 3.0" @@ -33,13 +33,13 @@ class CrossSpeciesComparisonGeneDetectPluginConan(ConanFile): default_options = {"shared": True, "fPIC": True} # Data plugin dependencies - requires = ("CrossSpeciesComparisonTreeData/latest@lkeb/stable") + requires = ("XSCTreeData/latest@lkeb/stable") # Qt requirement is inherited from hdps-core scm = { "type": "git", - "subfolder": "hdps/CrossSpeciesComparisonGeneDetectPlugin", + "subfolder": "hdps/XSCGeneDetectPlugin", "url": "auto", "revision": "auto", } @@ -106,7 +106,7 @@ def generate(self): tc.variables["ManiVault_DIR"] = manivault_dir # Give the installation directory to CMake - MV_CSCTD_PATH = pathlib.Path(self.deps_cpp_info["CrossSpeciesComparisonTreeData"].rootpath).as_posix() + MV_CSCTD_PATH = pathlib.Path(self.deps_cpp_info["XSCTreeData"].rootpath).as_posix() tc.variables["MV_CSCTD_INSTALL_DIR"] = MV_CSCTD_PATH # Set some build options @@ -116,7 +116,7 @@ def generate(self): def _configure_cmake(self): cmake = CMake(self) - cmake.configure(build_script_folder="hdps/CrossSpeciesComparisonGeneDetectPlugin") + cmake.configure(build_script_folder="hdps/XSCGeneDetectPlugin") cmake.verbose = True return cmake diff --git a/res/CrossSpeciesComparisonGeneDetect_chart.qrc b/res/XSCGeneDetect_chart.qrc similarity index 100% rename from res/CrossSpeciesComparisonGeneDetect_chart.qrc rename to res/XSCGeneDetect_chart.qrc diff --git a/src/SettingsAction.Analysis.inl b/src/SettingsAction.Analysis.inl index 4eb2413..9886cf9 100644 --- a/src/SettingsAction.Analysis.inl +++ b/src/SettingsAction.Analysis.inl @@ -1294,7 +1294,7 @@ void SettingsAction::precomputeTreesFromHierarchy() auto middleClusterNamesDataset = mv::data().getDataset(_middleClusterNamesDataset.getCurrentDataset().getDatasetId()); auto bottomClusterNamesDataset = mv::data().getDataset(_bottomClusterNamesDataset.getCurrentDataset().getDatasetId()); - auto referenceTreeDataset = mv::data().getDataset(_referenceTreeDataset.getCurrentDataset().getDatasetId()); + auto referenceTreeDataset = mv::data().getDataset(_referenceTreeDataset.getCurrentDataset().getDatasetId()); QJsonObject speciesDataJson = referenceTreeDataset->getTreeData(); QStringList speciesNamesVerify = referenceTreeDataset->getTreeLeafNames(); if (speciesDataJson.isEmpty() || speciesNamesVerify.isEmpty()) diff --git a/src/SettingsAction.Serialization.inl b/src/SettingsAction.Serialization.inl index 0c64db3..0483611 100644 --- a/src/SettingsAction.Serialization.inl +++ b/src/SettingsAction.Serialization.inl @@ -1,5 +1,5 @@ inline SettingsAction::OptionSelectionAction::OptionSelectionAction(SettingsAction& SettingsAction) : - GroupAction(nullptr, "CrossSpeciesComparisonGeneDetectPluginOptionSelectionAction"), + GroupAction(nullptr, "XSCGeneDetectPluginOptionSelectionAction"), _settingsAction(SettingsAction) { setText("Options"); diff --git a/src/SettingsAction.Tree.inl b/src/SettingsAction.Tree.inl index a586e9e..39f15c7 100644 --- a/src/SettingsAction.Tree.inl +++ b/src/SettingsAction.Tree.inl @@ -216,7 +216,7 @@ QString SettingsAction::createJsonTreeFromNewick(QString tree, std::vectorgetDataType() == PointType; }); _filteringEditTreeDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == CrossSpeciesComparisonTreeType; + return dataset->getDataType() == XSCTreeType; }); _referenceTreeDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == CrossSpeciesComparisonTreeType; + return dataset->getDataType() == XSCTreeType; }); _mainPointsDataset.setFilterFunction([this](mv::Dataset dataset) -> bool { return dataset->getDataType() == PointType; @@ -432,7 +432,7 @@ SettingsAction::SettingsAction(CrossSpeciesComparisonGeneDetectPlugin& CrossSpec if (_filteringEditTreeDataset.getCurrentDataset().isValid()) { - auto treeDataset = mv::data().getDataset(_filteringEditTreeDataset.getCurrentDataset().getDatasetId()); + auto treeDataset = mv::data().getDataset(_filteringEditTreeDataset.getCurrentDataset().getDatasetId()); QStringList selectedRowsStrList = _geneNamesConnection.getString().split("*%$@*@$%*"); @@ -960,7 +960,7 @@ SettingsAction::SettingsAction(CrossSpeciesComparisonGeneDetectPlugin& CrossSpec auto referenceTreeDataset = _referenceTreeDataset.getCurrentDataset(); if (referenceTreeDataset.isValid()) { - auto referenceTree = mv::data().getDataset(referenceTreeDataset.getDatasetId()); + auto referenceTree = mv::data().getDataset(referenceTreeDataset.getDatasetId()); if (referenceTree.isValid()) { QString speciesData = _precomputedTreesFromTheHierarchy[clusterLevel][clusterName][geneName]; QJsonObject speciesDataJson = QJsonDocument::fromJson(speciesData.toUtf8()).object(); diff --git a/src/SettingsAction.cpp b/src/SettingsAction.cpp index 334bc3c..351fd23 100644 --- a/src/SettingsAction.cpp +++ b/src/SettingsAction.cpp @@ -1,10 +1,10 @@ #include "SettingsAction.h" -#include "CrossSpeciesComparisonGeneDetectPlugin.h" +#include "XSCGeneDetectPlugin.h" #include #include #include #include -#include +#include #include // for std::reduce //#include "lib/Distance/annoylib.h" //#include "lib/Distance/kissrandom.h" diff --git a/src/SettingsAction.h b/src/SettingsAction.h index bd98867..8cbff18 100644 --- a/src/SettingsAction.h +++ b/src/SettingsAction.h @@ -44,7 +44,7 @@ #include using namespace mv::gui; class QMenu; -class CrossSpeciesComparisonGeneDetectPlugin; +class XSCGeneDetectPlugin; class FetchMetaData; namespace mv @@ -227,7 +227,7 @@ class SettingsAction : public WidgetAction }; public: - SettingsAction(CrossSpeciesComparisonGeneDetectPlugin& CrossSpeciesComparisonGeneDetectPlugins); + SettingsAction(XSCGeneDetectPlugin& XSCGeneDetectPlugins); public: // Action getters @@ -380,7 +380,7 @@ class SettingsAction : public WidgetAction QVariantMap toVariantMap() const override; protected: - CrossSpeciesComparisonGeneDetectPlugin& _crossSpeciesComparisonGeneDetectPlugin; + XSCGeneDetectPlugin& _XSCGeneDetectPlugin; VariantAction _listModel; StringAction _selectedGene; DatasetPickerAction _filteringEditTreeDataset; diff --git a/src/CrossSpeciesComparisonGeneDetectPlugin.cpp b/src/XSCGeneDetectPlugin.cpp similarity index 97% rename from src/CrossSpeciesComparisonGeneDetectPlugin.cpp rename to src/XSCGeneDetectPlugin.cpp index 13b14c4..1beabe1 100644 --- a/src/CrossSpeciesComparisonGeneDetectPlugin.cpp +++ b/src/XSCGeneDetectPlugin.cpp @@ -1,9 +1,9 @@ -#include "CrossSpeciesComparisonGeneDetectPlugin.h" +#include "XSCGeneDetectPlugin.h" #include #include -#include +#include #include #include #include @@ -23,7 +23,7 @@ #ifdef _WIN32 #include #endif -Q_PLUGIN_METADATA(IID "studio.manivault.CrossSpeciesComparisonGeneDetectPlugin") +Q_PLUGIN_METADATA(IID "studio.manivault.XSCGeneDetectPlugin") using namespace mv; @@ -251,14 +251,14 @@ std::map convertToStatisticsMap(const QString& for -CrossSpeciesComparisonGeneDetectPlugin::CrossSpeciesComparisonGeneDetectPlugin(const PluginFactory* factory) : +XSCGeneDetectPlugin::XSCGeneDetectPlugin(const PluginFactory* factory) : ViewPlugin(factory), _settingsAction(*this) { } -void CrossSpeciesComparisonGeneDetectPlugin::init() +void XSCGeneDetectPlugin::init() { auto& shortcuts = getShortcuts(); @@ -284,7 +284,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::init() if (_settingsAction.getFilteringEditTreeDatasetAction().getCurrentDataset().isValid()) { - auto treeDataset = mv::data().getDataset(_settingsAction.getFilteringEditTreeDatasetAction().getCurrentDataset().getDatasetId()); + auto treeDataset = mv::data().getDataset(_settingsAction.getFilteringEditTreeDatasetAction().getCurrentDataset().getDatasetId()); QStringList selectedRowsStrList = _settingsAction.getSelectedRowIndexAction().getString().split(","); QList selectedRows; @@ -796,13 +796,6 @@ void CrossSpeciesComparisonGeneDetectPlugin::init() - - - - - - - auto mainLayout = new QVBoxLayout(); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); @@ -978,7 +971,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::init() } -void CrossSpeciesComparisonGeneDetectPlugin::geneExplorer() +void XSCGeneDetectPlugin::geneExplorer() { std::vector selectedSpeciesIndices; auto speciesDataset = _settingsAction.getSpeciesNamesDataset().getCurrentDataset(); @@ -1159,7 +1152,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::geneExplorer() } -void CrossSpeciesComparisonGeneDetectPlugin::geneExplorer(QString selectedSpecies) +void XSCGeneDetectPlugin::geneExplorer(QString selectedSpecies) { std::vector selectedSpeciesIndices; auto speciesDataset = _settingsAction.getSpeciesNamesDataset().getCurrentDataset(); @@ -1340,7 +1333,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::geneExplorer(QString selectedSpecie } } -void CrossSpeciesComparisonGeneDetectPlugin::adjustTableWidths(const QString& value) { +void XSCGeneDetectPlugin::adjustTableWidths(const QString& value) { /* // Assuming _settingsAction.getHorizontalLayout() returns your QHBoxLayout QHBoxLayout* layout = _settingsAction.getTableSplitter(); @@ -1394,7 +1387,7 @@ QColor getColorFromValue(int value, int min, int max) { -void CrossSpeciesComparisonGeneDetectPlugin::modifyListData() +void XSCGeneDetectPlugin::modifyListData() { try { //qDebug() << "It's here"; @@ -1692,7 +1685,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::modifyListData() auto referenceTreeDataset = _settingsAction.getReferenceTreeDatasetAction().getCurrentDataset(); if (referenceTreeDataset.isValid()) { - auto referenceTree = mv::data().getDataset(referenceTreeDataset.getDatasetId()); + auto referenceTree = mv::data().getDataset(referenceTreeDataset.getDatasetId()); if (referenceTree.isValid()) { QJsonObject speciesDataJson = referenceTree->getTreeData(); updateSpeciesData(speciesDataJson, speciesExpressionMap); @@ -1818,7 +1811,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::modifyListData() } -void CrossSpeciesComparisonGeneDetectPlugin::selectedCellCountStatusBarAdd() +void XSCGeneDetectPlugin::selectedCellCountStatusBarAdd() { if (!_settingsAction.getSelectedSpeciesCellCountMap().empty()) { @@ -2113,7 +2106,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::selectedCellCountStatusBarAdd() -void CrossSpeciesComparisonGeneDetectPlugin::selectedCellStatisticsStatusBarAdd(std::map statisticsValues, QStringList selectedSpecies) +void XSCGeneDetectPlugin::selectedCellStatisticsStatusBarAdd(std::map statisticsValues, QStringList selectedSpecies) { if (!_settingsAction.getSelectedSpeciesCellCountMap().empty()) { @@ -2603,7 +2596,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::selectedCellStatisticsStatusBarAdd( adjustTableWidths("large"); } -void CrossSpeciesComparisonGeneDetectPlugin::updatePhylogeneticTree() +void XSCGeneDetectPlugin::updatePhylogeneticTree() { if (_settingsAction.getGeneTableView()) { @@ -2630,7 +2623,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::updatePhylogeneticTree() auto referenceTreeDataset = _settingsAction.getReferenceTreeDatasetAction().getCurrentDataset(); if (referenceTreeDataset.isValid()) { - auto referenceTree = mv::data().getDataset(referenceTreeDataset.getDatasetId()); + auto referenceTree = mv::data().getDataset(referenceTreeDataset.getDatasetId()); if (referenceTree.isValid()) { QJsonObject speciesDataJson = referenceTree->getTreeData(); updateTreeData(speciesDataJson, statisticsValues); @@ -2650,16 +2643,16 @@ void CrossSpeciesComparisonGeneDetectPlugin::updatePhylogeneticTree() } } -void CrossSpeciesComparisonGeneDetectPlugin::selectedCellCountStatusBarRemove() +void XSCGeneDetectPlugin::selectedCellCountStatusBarRemove() { _settingsAction.getSelectionDetailsTable()->setModel(new QStandardItemModel()); } -void CrossSpeciesComparisonGeneDetectPlugin::selectedCellStatisticsStatusBarRemove() +void XSCGeneDetectPlugin::selectedCellStatisticsStatusBarRemove() { _settingsAction.getSelectionDetailsTable()->setModel(new QStandardItemModel()); } -void CrossSpeciesComparisonGeneDetectPlugin::updateSpeciesData(QJsonObject& node, const std::map& speciesExpressionMap) { +void XSCGeneDetectPlugin::updateSpeciesData(QJsonObject& node, const std::map& speciesExpressionMap) { // Check if the "name" key exists in the current node if (node.contains("name")) { QString nodeName = node["name"].toString(); @@ -2754,7 +2747,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::updateSpeciesData(QJsonObject& node } } -void CrossSpeciesComparisonGeneDetectPlugin::updateTreeData(QJsonObject& node, const std::map& speciesExpressionMap) { +void XSCGeneDetectPlugin::updateTreeData(QJsonObject& node, const std::map& speciesExpressionMap) { // Check if the "name" key exists in the current node if (node.contains("name")) { QString nodeName = node["name"].toString(); @@ -2847,7 +2840,7 @@ void CrossSpeciesComparisonGeneDetectPlugin::updateTreeData(QJsonObject& node, c } } /* -void CrossSpeciesComparisonGeneDetectPlugin::onDataEvent(mv::DatasetEvent* dataEvent) +void XSCGeneDetectPlugin::onDataEvent(mv::DatasetEvent* dataEvent) { // Get smart pointer to dataset that changed const auto changedDataSet = dataEvent->getDataset(); @@ -2915,18 +2908,18 @@ void CrossSpeciesComparisonGeneDetectPlugin::onDataEvent(mv::DatasetEvent* dataE } */ -void CrossSpeciesComparisonGeneDetectPlugin::fromVariantMap(const QVariantMap& variantMap) +void XSCGeneDetectPlugin::fromVariantMap(const QVariantMap& variantMap) { ViewPlugin::fromVariantMap(variantMap); - mv::util::variantMapMustContain(variantMap, "CSCGDV:CrossSpeciesComparison Gene Detect Plugin Settings"); - _settingsAction.fromVariantMap(variantMap["CSCGDV:CrossSpeciesComparison Gene Detect Plugin Settings"].toMap()); + mv::util::variantMapMustContain(variantMap, "CSCGDV:XSC Gene Detect Plugin Settings"); + _settingsAction.fromVariantMap(variantMap["CSCGDV:XSC Gene Detect Plugin Settings"].toMap()); // modifyTableData(); // _settingsAction.getStartComputationTriggerAction().trigger(); } -QVariantMap CrossSpeciesComparisonGeneDetectPlugin::toVariantMap() const +QVariantMap XSCGeneDetectPlugin::toVariantMap() const { QVariantMap variantMap = ViewPlugin::toVariantMap(); @@ -2934,12 +2927,12 @@ QVariantMap CrossSpeciesComparisonGeneDetectPlugin::toVariantMap() const return variantMap; } -ViewPlugin* CrossSpeciesComparisonGeneDetectPluginFactory::produce() +ViewPlugin* XSCGeneDetectPluginFactory::produce() { - return new CrossSpeciesComparisonGeneDetectPlugin(this); + return new XSCGeneDetectPlugin(this); } -mv::DataTypes CrossSpeciesComparisonGeneDetectPluginFactory::supportedDataTypes() const +mv::DataTypes XSCGeneDetectPluginFactory::supportedDataTypes() const { DataTypes supportedTypes; @@ -2949,18 +2942,18 @@ mv::DataTypes CrossSpeciesComparisonGeneDetectPluginFactory::supportedDataTypes( return supportedTypes; } -mv::gui::PluginTriggerActions CrossSpeciesComparisonGeneDetectPluginFactory::getPluginTriggerActions(const mv::Datasets& datasets) const +mv::gui::PluginTriggerActions XSCGeneDetectPluginFactory::getPluginTriggerActions(const mv::Datasets& datasets) const { PluginTriggerActions pluginTriggerActions; /* - const auto getPluginInstance = [this]() -> CrossSpeciesComparisonGeneDetectPlugin* { - return dynamic_cast(plugins().requestViewPlugin(getKind())); + const auto getPluginInstance = [this]() -> XSCGeneDetectPlugin* { + return dynamic_cast(plugins().requestViewPlugin(getKind())); }; const auto numberOfDatasets = datasets.count(); if (numberOfDatasets >= 1 && PluginFactory::areAllDatasetsOfTheSameType(datasets, PointType)) { - auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, "CrossSpeciesComparisonGeneDetect View", "View gene data", getIcon(), [this, getPluginInstance, datasets](PluginTriggerAction& pluginTriggerAction) -> void { + auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, "XSCGeneDetect View", "View gene data", getIcon(), [this, getPluginInstance, datasets](PluginTriggerAction& pluginTriggerAction) -> void { for (auto dataset : datasets) getPluginInstance(); }); diff --git a/src/CrossSpeciesComparisonGeneDetectPlugin.h b/src/XSCGeneDetectPlugin.h similarity index 84% rename from src/CrossSpeciesComparisonGeneDetectPlugin.h rename to src/XSCGeneDetectPlugin.h index d6e29ed..21698cd 100644 --- a/src/CrossSpeciesComparisonGeneDetectPlugin.h +++ b/src/XSCGeneDetectPlugin.h @@ -22,7 +22,7 @@ using namespace mv::util; class QLabel; -class CrossSpeciesComparisonGeneDetectPlugin : public ViewPlugin +class XSCGeneDetectPlugin : public ViewPlugin { Q_OBJECT @@ -32,10 +32,10 @@ class CrossSpeciesComparisonGeneDetectPlugin : public ViewPlugin * Constructor * @param factory Pointer to the plugin factory */ - CrossSpeciesComparisonGeneDetectPlugin(const PluginFactory* factory); + XSCGeneDetectPlugin(const PluginFactory* factory); /** Destructor */ - ~CrossSpeciesComparisonGeneDetectPlugin() override = default; + ~XSCGeneDetectPlugin() override = default; /** This function is called by the core after the view plugin has been created */ void init() override; @@ -84,24 +84,24 @@ class CrossSpeciesComparisonGeneDetectPlugin : public ViewPlugin }; /** - * CrossSpeciesComparisonGeneDetect plugin factory class + * XSCGeneDetect plugin factory class * * Note: Factory does not need to be altered (merely responsible for generating new plugins when requested) */ -class CrossSpeciesComparisonGeneDetectPluginFactory : public ViewPluginFactory +class XSCGeneDetectPluginFactory : public ViewPluginFactory { Q_INTERFACES(mv::plugin::ViewPluginFactory mv::plugin::PluginFactory) Q_OBJECT - Q_PLUGIN_METADATA(IID "studio.manivault.CrossSpeciesComparisonGeneDetectPlugin" - FILE "CrossSpeciesComparisonGeneDetectPlugin.json") + Q_PLUGIN_METADATA(IID "studio.manivault.XSCGeneDetectPlugin" + FILE "XSCGeneDetectPlugin.json") public: /** Default constructor */ - CrossSpeciesComparisonGeneDetectPluginFactory() {} + XSCGeneDetectPluginFactory() {} /** Destructor */ - ~CrossSpeciesComparisonGeneDetectPluginFactory() override {} + ~XSCGeneDetectPluginFactory() override {} /** Creates an instance of the example view plugin */ ViewPlugin* produce() override; diff --git a/src/CrossSpeciesComparisonGeneDetectPlugin.json b/src/XSCGeneDetectPlugin.json similarity index 54% rename from src/CrossSpeciesComparisonGeneDetectPlugin.json rename to src/XSCGeneDetectPlugin.json index d866922..0a0027d 100644 --- a/src/CrossSpeciesComparisonGeneDetectPlugin.json +++ b/src/XSCGeneDetectPlugin.json @@ -1,5 +1,5 @@ { - "name" : "CrossSpeciesComparisonGeneDetect", + "name" : "XSCGeneDetect", "version" : "1.1", "dependencies" : ["Points"] } From 6067761bfb9090d9228ac05ff7f715d01b2f81de Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu Date: Tue, 21 Jul 2026 18:47:38 +0200 Subject: [PATCH 09/10] update conan file --- conanfile.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/conanfile.py b/conanfile.py index 97bc147..4773af2 100644 --- a/conanfile.py +++ b/conanfile.py @@ -33,7 +33,7 @@ class XSCGeneDetectPluginConan(ConanFile): default_options = {"shared": True, "fPIC": True} # Data plugin dependencies - requires = ("XSCTreeData/latest@lkeb/stable") + #requires = ("XSCTreeData/latest@lkeb/stable") # Qt requirement is inherited from hdps-core @@ -51,6 +51,32 @@ def __get_git_path(self): print(f"git info from {path}") return path + def _dependency_channel(self): + branch = ( + os.getenv("GITHUB_HEAD_REF") + or os.getenv("GITHUB_REF_NAME") + ) + + if not branch: + branch = subprocess.check_output( + [ + "git", + "-C", + self.__get_git_path(), + "rev-parse", + "--abbrev-ref", + "HEAD", + ], + text=True, + ).strip() + + self.output.info(f"Detected branch: {branch}") + + if branch in ("main", "master", "HEAD"): + return "latest" + + return branch.rsplit("/", 1)[-1] + def export(self): print("In export") # save the original source path to the directory used to build the package @@ -66,8 +92,14 @@ def set_version(self): # print(f"Got version: {self.version}") def requirements(self): + channel = self._dependency_channel() + + self.output.info(f"Using XSCTreeData channel: {channel}") + + self.requires(f"XSCTreeData/{channel}@lkeb/stable") + branch_info = PluginBranchInfo(self.__get_git_path()) - print(f"Core requirement {branch_info.core_requirement}") + self.output.info(f"Core requirement {branch_info.core_requirement}") self.requires(branch_info.core_requirement) def configure(self): From b994b4ecdf59121ae914f1c65d626b1e8a964a72 Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu <44787782+sbvis@users.noreply.github.com> Date: Tue, 21 Jul 2026 19:02:17 +0200 Subject: [PATCH 10/10] Update conanfile.py --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 4773af2..e9ff8e8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -21,7 +21,7 @@ class XSCGeneDetectPluginConan(ConanFile): description = """Viewer of cell XSCTreeData data as described in a .swc file.""" topics = ("manivault", "plugin", "view", "XSCGeneDetectPlugin") url = "https://github.com/ManiVaultStudio/XSCGeneDetectPlugin" - author = "julianthijssen@gmail.com" # conan recipe author + author = "sbasu" # conan recipe author license = "LGPL 3.0" short_paths = True