10#include <QStandardItemModel>
27 gDetectorConstruction(dc),
31 dbhost = gopts->getRequiredScalarString(
"sql");
32 experiment = gopts->getRequiredScalarString(
"experiment");
38 std::vector<std::string> dirs = {
46 log->warning(
"Failed to find database file <%1>. Setup tab will start empty.", dbhost);
48 experimentModel->blockSignals(
true);
49 experimentModel->setHorizontalHeaderLabels(
50 QStringList() << tr(
"exp/system") << tr(
"volumes") << tr(
"variation") << tr(
"run"));
51 experimentModel->blockSignals(
false);
54 experimentHeaderLabel->setText(tr(
"Database \"%1\" was not found.").arg(QString::fromStdString(dbhost)));
59 if (sqlite3_open_v2(dbPath.value().c_str(), &db, SQLITE_OPEN_READONLY,
nullptr) != SQLITE_OK || !isGeometryTableValid()) {
62 log->warning(
"Failed to open or validate database <%1>. Setup tab will start empty.", dbhost);
64 experimentModel->blockSignals(
true);
65 experimentModel->setHorizontalHeaderLabels(
66 QStringList() << tr(
"exp/system") << tr(
"volumes") << tr(
"variation") << tr(
"run"));
67 experimentModel->blockSignals(
false);
70 experimentHeaderLabel->setText(
71 tr(
"Database \"%1\" could not be opened or validated.").arg(QString::fromStdString(dbhost)));
75 log->info(1,
"Opened database: %1 found at %2", dbhost, dbPath.value());
82 experimentModel->blockSignals(
true);
86 bool expFound =
false;
87 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
88 QStandardItem* expItem = experimentModel->item(i, 0);
89 if (expItem && expItem->text() == QString::fromStdString(experiment)) {
90 expItem->setCheckState(Qt::Checked);
100 applyGSystemSelections();
103 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
104 QStandardItem* expItem = experimentModel->item(i, 0);
105 for (
int j = 0; j < expItem->rowCount(); ++j) {
106 QStandardItem* sysItem = expItem->child(j, 0);
107 updateSystemItemAppearance(sysItem);
112 experimentModel->blockSignals(
false);
117 QTimer::singleShot(0,
this, &DBSelectView::resizeExperimentColumns);
120bool DBSelectView::isGeometryTableValid()
const {
124 sqlite3_stmt* stmt =
nullptr;
125 const char* sql_query =
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='geometry'";
128 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) != SQLITE_OK) {
134 bool tableExists =
false;
135 if (sqlite3_step(stmt) == SQLITE_ROW) {
136 tableExists = sqlite3_column_int(stmt, 0) > 0;
138 sqlite3_finalize(stmt);
144 sql_query =
"SELECT COUNT(*) FROM geometry";
145 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) != SQLITE_OK) {
151 bool hasData =
false;
152 if (sqlite3_step(stmt) == SQLITE_ROW) {
153 hasData = sqlite3_column_int(stmt, 0) > 0;
155 sqlite3_finalize(stmt);
160void DBSelectView::applyGSystemSelections() {
164 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
165 QStandardItem* expItem = experimentModel->item(i, 0);
170 if (expItem->text() == QString::fromStdString(experiment)) {
171 expItem->setCheckState(Qt::Checked);
175 for (
int j = 0; j < expItem->rowCount(); ++j) {
176 QStandardItem* sysItem = expItem->child(j, 0);
177 QStandardItem* varItem = expItem->child(j, 2);
178 QStandardItem* runItem = expItem->child(j, 3);
179 if (!sysItem || !varItem || !runItem)
182 std::string sysName = sysItem->text().toStdString();
183 std::string rowVariation = varItem->data(Qt::EditRole).toString().toStdString();
184 bool systemFound =
false;
186 for (
auto const& gsys : gsystems) {
187 if (gsys->getName() == sysName && gsys->getVariation() == rowVariation) {
189 sysItem->setCheckState(Qt::Checked);
192 QStringList availableRuns = getAvailableRuns(sysName, rowVariation);
193 QString selectedRun = QString::number(gsys->getRunno());
194 if (availableRuns.contains(selectedRun))
195 runItem->setData(selectedRun, Qt::EditRole);
196 else if (!availableRuns.isEmpty())
197 runItem->setData(availableRuns.first(), Qt::EditRole);
198 runItem->setData(availableRuns, Qt::UserRole);
200 updateSystemItemAppearance(sysItem);
207 sysItem->setCheckState(Qt::Unchecked);
213void DBSelectView::setupUI() {
214 auto mainLayout =
new QVBoxLayout(
this);
215 mainLayout->setContentsMargins(10, 10, 10, 10);
216 mainLayout->setSpacing(10);
219 auto headerLayout =
new QHBoxLayout();
221 auto labelLayout =
new QVBoxLayout();
222 titleLabel =
new QLabel(tr(
"Experiment Selection"),
this);
223 QFont titleFont(
"Avenir", 20, QFont::Bold);
224 titleLabel->setFont(titleFont);
225 titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
226 labelLayout->addWidget(titleLabel);
228 experimentHeaderLabel =
new QLabel(
"",
this);
229 experimentHeaderLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
230 experimentHeaderLabel->setWordWrap(
true);
231 experimentHeaderLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
232 labelLayout->addWidget(experimentHeaderLabel);
234 headerLayout->addLayout(labelLayout);
235 headerLayout->addStretch();
236 reloadButton =
new QPushButton(tr(
"Reload"),
this);
237 reloadButton->setEnabled(
false);
238 headerLayout->addWidget(reloadButton);
241 mainLayout->addLayout(headerLayout);
244 experimentTree =
new QTreeView(
this);
245 experimentTree->setAlternatingRowColors(
true);
246 experimentTree->setSelectionMode(QAbstractItemView::SingleSelection);
247 experimentTree->setSelectionBehavior(QAbstractItemView::SelectRows);
248 experimentTree->header()->show();
250 experimentModel =
new QStandardItemModel(
this);
251 experimentModel->setHorizontalHeaderLabels(
252 QStringList() << tr(
"exp/system") << tr(
"volumes") << tr(
"variation") << tr(
"run"));
253 experimentTree->setModel(experimentModel);
256 experimentTree->setItemDelegateForColumn(3,
new ComboDelegate(
this));
258 mainLayout->addWidget(experimentTree);
260 connect(experimentModel, &QStandardItemModel::itemChanged,
261 this, &DBSelectView::onItemChanged);
264void DBSelectView::loadExperiments() {
265 experimentModel->clear();
268 const std::string configuredExperiment = experiment;
269 sqlite3_stmt* stmt =
nullptr;
270 const char* sql_query =
"SELECT DISTINCT experiment FROM geometry";
272 int rc = sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr);
273 if (rc != SQLITE_OK) {
278 while (sqlite3_step(stmt) == SQLITE_ROW) {
279 const char* expText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
281 QString expName = QString::fromUtf8(expText);
284 experiment = expName.toStdString();
286 auto* expItem =
new QStandardItem(expName);
287 expItem->setFlags(expItem->flags() & ~Qt::ItemIsEditable);
288 expItem->setCheckable(
true);
289 expItem->setCheckState(Qt::Unchecked);
292 auto* dummyEntries =
new QStandardItem(
"");
293 auto* dummyVar =
new QStandardItem(
"");
294 auto* dummyRun =
new QStandardItem(
"");
296 loadSystemsForExperiment(expItem);
298 experimentModel->appendRow(QList<QStandardItem*>() << expItem << dummyEntries << dummyVar << dummyRun);
302 sqlite3_finalize(stmt);
303 experiment = configuredExperiment;
306void DBSelectView::loadSystemsForExperiment(QStandardItem* experimentItem) {
307 sqlite3_stmt* stmt =
nullptr;
308 const char* sql_query =
"SELECT DISTINCT system, variation FROM geometry WHERE experiment = ? ORDER BY system, variation";
310 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
312 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
314 while (sqlite3_step(stmt) == SQLITE_ROW) {
315 const char* sysText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
316 const char* varText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
317 if (sysText && varText) {
318 auto* sysItem =
new QStandardItem(QString::fromUtf8(sysText));
319 sysItem->setFlags(sysItem->flags() & ~Qt::ItemIsEditable);
320 sysItem->setCheckable(
true);
321 sysItem->setCheckState(Qt::Unchecked);
324 auto* entriesItem =
new QStandardItem(
"");
327 auto* varItem =
new QStandardItem();
328 QString variation = QString::fromUtf8(varText);
329 QStringList varList{variation};
330 varItem->setData(variation, Qt::EditRole);
331 varItem->setData(varList, Qt::UserRole);
332 varItem->setFlags(varItem->flags() & ~Qt::ItemIsEditable);
335 auto* runItem =
new QStandardItem();
336 QStringList runList = getAvailableRuns(sysText, varText);
337 if (!runList.isEmpty())
338 runItem->setData(runList.first(), Qt::EditRole);
340 runItem->setData(
"", Qt::EditRole);
341 runItem->setData(runList, Qt::UserRole);
343 QList<QStandardItem*> rowItems;
344 rowItems << sysItem << entriesItem << varItem << runItem;
345 experimentItem->appendRow(rowItems);
350 sqlite3_finalize(stmt);
353int DBSelectView::getGeometryCount(
const std::string& system,
const std::string& variation,
int run)
const {
354 if (!db) {
return 0; }
357 std::string query =
"SELECT COUNT(*) FROM geometry WHERE experiment = ? AND system = ? AND variation = ? AND run = ?";
359 sqlite3_stmt* stmt =
nullptr;
360 if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt,
nullptr) == SQLITE_OK) {
361 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
362 sqlite3_bind_text(stmt, 2, system.c_str(), -1, SQLITE_TRANSIENT);
363 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
364 sqlite3_bind_int(stmt, 4, run);
366 if (sqlite3_step(stmt) == SQLITE_ROW) {
367 count = sqlite3_column_int(stmt, 0);
374 sqlite3_finalize(stmt);
378QStringList DBSelectView::getAvailableRuns(
const std::string& system,
const std::string& variation)
const {
380 if (!db) {
return runList; }
382 sqlite3_stmt* stmt =
nullptr;
383 const char* sql_query =
"SELECT DISTINCT run FROM geometry WHERE experiment = ? AND system = ? AND variation = ? ORDER BY run";
385 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
386 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
387 sqlite3_bind_text(stmt, 2, system.c_str(), -1, SQLITE_TRANSIENT);
388 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
390 while (sqlite3_step(stmt) == SQLITE_ROW) {
391 int runVal = sqlite3_column_int(stmt, 0);
392 runList << QString::number(runVal);
396 sqlite3_finalize(stmt);
400bool DBSelectView::systemAvailable(
const std::string& system,
const std::string& variation,
int run) {
401 if (!db) {
return false; }
403 std::string query =
"SELECT COUNT(*) FROM geometry WHERE system = ? AND variation = ? AND run = ?";
404 sqlite3_stmt* stmt =
nullptr;
406 if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt,
nullptr) != SQLITE_OK) {
411 sqlite3_bind_text(stmt, 1, system.c_str(), -1, SQLITE_TRANSIENT);
412 sqlite3_bind_text(stmt, 2, variation.c_str(), -1, SQLITE_TRANSIENT);
413 sqlite3_bind_int(stmt, 3, run);
415 bool available =
false;
416 if (sqlite3_step(stmt) == SQLITE_ROW) {
417 int count = sqlite3_column_int(stmt, 0);
418 available = (count > 0);
421 sqlite3_finalize(stmt);
425QIcon DBSelectView::createStatusIcon(
const QColor& color) {
426 QPixmap pixmap(12, 12);
428 return QIcon(pixmap);
431void DBSelectView::updateSystemItemAppearance(QStandardItem* systemItem) {
432 QStandardItem* parentItem = systemItem->parent();
437 int row = systemItem->row();
438 QStandardItem* varItem = parentItem->child(row, 2);
439 QStandardItem* runItem = parentItem->child(row, 3);
441 QString varStr = varItem ? varItem->data(Qt::EditRole).toString() :
"";
442 QString runStr = runItem ? runItem->data(Qt::EditRole).toString() :
"";
444 int run = runStr.toInt();
445 QString expStr = parentItem->text();
448 experiment = expStr.toStdString();
450 std::string systemName = systemItem->text().toStdString();
451 std::string variation = varStr.toStdString();
453 int count = getGeometryCount(systemName, variation, run);
456 QStandardItem* entriesItem = parentItem->child(row, 1);
458 entriesItem->setText(QString::number(count));
462 bool available = (count > 0);
463 QColor statusColor = available ? QColor(
"green") : QColor(
"red");
464 systemItem->setIcon(createStatusIcon(statusColor));
467 systemItem->setData(QVariant(), Qt::BackgroundRole);
468 systemItem->setData(QVariant(), Qt::ForegroundRole);
471void DBSelectView::updateExperimentHeader() {
472 QStandardItem* selectedExp =
nullptr;
475 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
476 QStandardItem* expItem = experimentModel->item(i, 0);
477 if (expItem && expItem->checkState() == Qt::Checked) {
478 selectedExp = expItem;
484 int totalSystems = selectedExp->rowCount();
485 experimentHeaderLabel->setText(tr(
"Total systems for experiment \"%1\": %2")
486 .arg(selectedExp->text()).arg(totalSystems));
489 experimentHeaderLabel->setText(
"");
493 experimentModel->setHorizontalHeaderLabels(
494 QStringList() << tr(
"exp/system") << tr(
"volumes") << tr(
"variation") << tr(
"run"));
497void DBSelectView::onItemChanged(QStandardItem* item) {
498 if (m_ignoreItemChange || !item)
502 m_ignoreItemChange =
true;
505 if (!item->parent()) {
506 if (item->checkState() == Qt::Checked) {
508 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
509 QStandardItem* expItem = experimentModel->item(i, 0);
511 expItem->setCheckState(Qt::Unchecked);
513 updateExperimentHeader();
517 for (
int i = 0; i < item->rowCount(); ++i) {
518 QStandardItem* sysItem = item->child(i, 0);
520 sysItem->setCheckState(Qt::Unchecked);
522 updateExperimentHeader();
527 if (item->column() == 0) {
528 if (item->checkState() == Qt::Checked) {
529 for (
int i = 0; i < item->parent()->rowCount(); ++i) {
530 QStandardItem* sibling = item->parent()->child(i, 0);
531 if (sibling && sibling != item && sibling->text() == item->text()) {
532 sibling->setCheckState(Qt::Unchecked);
536 updateSystemItemAppearance(item);
538 else if (item->column() == 2 || item->column() == 3) {
539 QStandardItem* sysItem = item->parent()->child(item->row(), 0);
540 updateSystemItemAppearance(sysItem);
544 m_ignoreItemChange =
false;
557 for (
int i = 0; i < experimentModel->rowCount(); i++) {
558 QStandardItem* expItem = experimentModel->item(i, 0);
562 for (
int j = 0; j < expItem->rowCount(); j++) {
563 QStandardItem* sysItem = expItem->child(j, 0);
564 QStandardItem* varItem = expItem->child(j, 2);
565 QStandardItem* runItem = expItem->child(j, 3);
567 if (!sysItem || !varItem || !runItem)
570 if (sysItem->checkState() == Qt::Checked) {
571 std::string systemName = sysItem->text().toStdString();
572 std::string variation = varItem->data(Qt::EditRole).toString().toStdString();
573 int run = runItem->data(Qt::EditRole).toInt();
574 std::string expName = expItem->text().toStdString();
577 2,
"%1: adding systemName: %2 , variation: %3, for run:%4",
SFUNCTION_NAME, systemName,
580 updatedSystems.emplace_back(
581 std::make_shared<GSystem>(
594 return updatedSystems;
597void DBSelectView::updateModifiedUI() {
599 updateExperimentHeader();
602 titleLabel->setText(tr(
"Experiment Selection* (modified)"));
604 titleLabel->setText(tr(
"Experiment Selection"));
605 reloadButton->setEnabled(modified);
608 resizeExperimentColumns();
611void DBSelectView::resizeExperimentColumns() {
612 if (!experimentTree) {
return; }
614 experimentTree->resizeColumnToContents(0);
615 experimentTree->setColumnWidth(1, 100);
616 experimentTree->setColumnWidth(2, 150);
617 experimentTree->setColumnWidth(3, 150);
618 experimentTree->header()->setStretchLastSection(
false);
619 experimentTree->expandAll();
627 if (!reloaded_system.empty()) {
628 std::ostringstream gsystemYaml;
630 for (
size_t i = 0; i < reloaded_system.size(); ++i) {
631 const auto& gsys = reloaded_system[i];
632 if (i > 0) { gsystemYaml <<
", "; }
633 gsystemYaml <<
"{name: " << gsys->getName()
634 <<
", factory: " << gsys->getFactoryName()
635 <<
", variation: " << gsys->getVariation();
636 if (gsys->getAnnotations()) {
637 gsystemYaml <<
", annotations: " << *gsys->getAnnotations();
642 gopt->setOptionValueFromString(
"gsystem", gsystemYaml.str());
643 gopt->setOptionValueFromString(
"runno", std::to_string(reloaded_system.front()->getRunno()));
645 for (
auto& gsys : reloaded_system) {
652 gDetectorConstruction->reload_geometry(reloaded_system);
void geometryReloaded()
Emitted after detector construction has reloaded geometry from the selected systems.
SystemList get_gsystems()
Build and return the list of selected systems as a SystemList.
void geometryAboutToReload()
Emitted immediately before detector construction replaces the current geometry.
DBSelectView(const std::shared_ptr< GOptions > &gopts, GDetectorConstruction *dc, QWidget *parent=nullptr)
Construct the view and populate the experiment/system model from the database.
void reload_geometry()
Slot invoked by the Reload button to reload geometry based on current selections.
GBase(const std::shared_ptr< GOptions > &gopt, std::string logger_name="")
std::shared_ptr< GLogger > log
constexpr const char * DBSELECT_LOGGER
Logger name used by the dbselect module.
std::vector< SystemPtr > SystemList
constexpr int ERR_EXPERIMENTNOTFOUND
constexpr char GSYSTEMSQLITETFACTORYLABEL[]
constexpr int ERR_GSQLITEERROR
SystemList getSystems(const std::shared_ptr< GOptions > &gopts)
std::filesystem::path gemc_root()
std::optional< std::string > searchForFileInLocations(const std::vector< std::string > &locations, std::string_view filename)