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 <", dbhost,
">. Setup tab will start empty.");
48 experimentModel->blockSignals(
true);
49 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
50 experimentModel->blockSignals(
false);
53 experimentHeaderLabel->setText(QString(
"Database \"%1\" was not found.").arg(QString::fromStdString(dbhost)));
58 if (sqlite3_open_v2(dbPath.value().c_str(), &db, SQLITE_OPEN_READONLY,
nullptr) != SQLITE_OK || !isGeometryTableValid()) {
61 log->warning(
"Failed to open or validate database <", dbhost,
">. Setup tab will start empty.");
63 experimentModel->blockSignals(
true);
64 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
65 experimentModel->blockSignals(
false);
68 experimentHeaderLabel->setText(QString(
"Database \"%1\" could not be opened or validated.").arg(QString::fromStdString(dbhost)));
72 log->info(1,
"Opened database: " + dbhost,
" found at ", dbPath.value());
79 experimentModel->blockSignals(
true);
83 bool expFound =
false;
84 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
85 QStandardItem* expItem = experimentModel->item(i, 0);
86 if (expItem && expItem->text() == QString::fromStdString(experiment)) {
87 expItem->setCheckState(Qt::Checked);
97 applyGSystemSelections();
100 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
101 QStandardItem* expItem = experimentModel->item(i, 0);
102 for (
int j = 0; j < expItem->rowCount(); ++j) {
103 QStandardItem* sysItem = expItem->child(j, 0);
104 updateSystemItemAppearance(sysItem);
109 experimentModel->blockSignals(
false);
114 QTimer::singleShot(0,
this, &DBSelectView::resizeExperimentColumns);
117bool DBSelectView::isGeometryTableValid()
const {
121 sqlite3_stmt* stmt =
nullptr;
122 const char* sql_query =
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='geometry'";
125 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) != SQLITE_OK) {
127 "SQL Error: Failed to check geometry table existence:", sqlite3_errmsg(db));
130 bool tableExists =
false;
131 if (sqlite3_step(stmt) == SQLITE_ROW) {
132 tableExists = sqlite3_column_int(stmt, 0) > 0;
134 sqlite3_finalize(stmt);
140 sql_query =
"SELECT COUNT(*) FROM geometry";
141 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) != SQLITE_OK) {
143 "SQL Error: Failed to count rows in geometry table:", sqlite3_errmsg(db));
146 bool hasData =
false;
147 if (sqlite3_step(stmt) == SQLITE_ROW) {
148 hasData = sqlite3_column_int(stmt, 0) > 0;
150 sqlite3_finalize(stmt);
155void DBSelectView::applyGSystemSelections() {
159 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
160 QStandardItem* expItem = experimentModel->item(i, 0);
165 if (expItem->text() == QString::fromStdString(experiment)) {
166 expItem->setCheckState(Qt::Checked);
170 for (
int j = 0; j < expItem->rowCount(); ++j) {
171 QStandardItem* sysItem = expItem->child(j, 0);
172 QStandardItem* varItem = expItem->child(j, 2);
173 QStandardItem* runItem = expItem->child(j, 3);
174 if (!sysItem || !varItem || !runItem)
177 std::string sysName = sysItem->text().toStdString();
178 std::string rowVariation = varItem->data(Qt::EditRole).toString().toStdString();
179 bool systemFound =
false;
181 for (
auto const& gsys : gsystems) {
182 if (gsys->getName() == sysName && gsys->getVariation() == rowVariation) {
184 sysItem->setCheckState(Qt::Checked);
187 QStringList availableRuns = getAvailableRuns(sysName, rowVariation);
188 QString selectedRun = QString::number(gsys->getRunno());
189 if (availableRuns.contains(selectedRun))
190 runItem->setData(selectedRun, Qt::EditRole);
191 else if (!availableRuns.isEmpty())
192 runItem->setData(availableRuns.first(), Qt::EditRole);
193 runItem->setData(availableRuns, Qt::UserRole);
195 updateSystemItemAppearance(sysItem);
202 sysItem->setCheckState(Qt::Unchecked);
208void DBSelectView::setupUI() {
209 auto mainLayout =
new QVBoxLayout(
this);
210 mainLayout->setContentsMargins(10, 10, 10, 10);
211 mainLayout->setSpacing(10);
214 auto headerLayout =
new QHBoxLayout();
216 auto labelLayout =
new QVBoxLayout();
218 titleLabel =
new QLabel(
"Experiment Selection",
this);
219 QFont titleFont(
"Avenir", 20, QFont::Bold);
220 titleLabel->setFont(titleFont);
221 titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
222 labelLayout->addWidget(titleLabel);
224 experimentHeaderLabel =
new QLabel(
"",
this);
225 experimentHeaderLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
226 experimentHeaderLabel->setWordWrap(
true);
227 experimentHeaderLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
228 labelLayout->addWidget(experimentHeaderLabel);
230 headerLayout->addLayout(labelLayout);
231 headerLayout->addStretch();
233 reloadButton =
new QPushButton(
"Reload",
this);
234 reloadButton->setEnabled(
false);
235 headerLayout->addWidget(reloadButton);
238 mainLayout->addLayout(headerLayout);
241 experimentTree =
new QTreeView(
this);
242 experimentTree->setAlternatingRowColors(
true);
243 experimentTree->setSelectionMode(QAbstractItemView::SingleSelection);
244 experimentTree->setSelectionBehavior(QAbstractItemView::SelectRows);
245 experimentTree->header()->show();
247 experimentModel =
new QStandardItemModel(
this);
248 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
250 experimentTree->setModel(experimentModel);
253 experimentTree->setItemDelegateForColumn(3,
new ComboDelegate(
this));
255 mainLayout->addWidget(experimentTree);
257 connect(experimentModel, &QStandardItemModel::itemChanged,
258 this, &DBSelectView::onItemChanged);
261void DBSelectView::loadExperiments() {
262 experimentModel->clear();
265 const std::string configuredExperiment = experiment;
266 sqlite3_stmt* stmt =
nullptr;
267 const char* sql_query =
"SELECT DISTINCT experiment FROM geometry";
269 int rc = sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr);
270 if (rc != SQLITE_OK) {
275 while (sqlite3_step(stmt) == SQLITE_ROW) {
276 const char* expText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
278 QString expName = QString::fromUtf8(expText);
281 experiment = expName.toStdString();
283 auto* expItem =
new QStandardItem(expName);
284 expItem->setFlags(expItem->flags() & ~Qt::ItemIsEditable);
285 expItem->setCheckable(
true);
286 expItem->setCheckState(Qt::Unchecked);
289 auto* dummyEntries =
new QStandardItem(
"");
290 auto* dummyVar =
new QStandardItem(
"");
291 auto* dummyRun =
new QStandardItem(
"");
293 loadSystemsForExperiment(expItem);
295 experimentModel->appendRow(QList<QStandardItem*>() << expItem << dummyEntries << dummyVar << dummyRun);
299 sqlite3_finalize(stmt);
300 experiment = configuredExperiment;
303void DBSelectView::loadSystemsForExperiment(QStandardItem* experimentItem) {
304 sqlite3_stmt* stmt =
nullptr;
305 const char* sql_query =
"SELECT DISTINCT system, variation FROM geometry WHERE experiment = ? ORDER BY system, variation";
307 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
309 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
311 while (sqlite3_step(stmt) == SQLITE_ROW) {
312 const char* sysText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
313 const char* varText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
314 if (sysText && varText) {
315 auto* sysItem =
new QStandardItem(QString::fromUtf8(sysText));
316 sysItem->setFlags(sysItem->flags() & ~Qt::ItemIsEditable);
317 sysItem->setCheckable(
true);
318 sysItem->setCheckState(Qt::Unchecked);
321 auto* entriesItem =
new QStandardItem(
"");
324 auto* varItem =
new QStandardItem();
325 QString variation = QString::fromUtf8(varText);
326 QStringList varList{variation};
327 varItem->setData(variation, Qt::EditRole);
328 varItem->setData(varList, Qt::UserRole);
329 varItem->setFlags(varItem->flags() & ~Qt::ItemIsEditable);
332 auto* runItem =
new QStandardItem();
333 QStringList runList = getAvailableRuns(sysText, varText);
334 if (!runList.isEmpty())
335 runItem->setData(runList.first(), Qt::EditRole);
337 runItem->setData(
"", Qt::EditRole);
338 runItem->setData(runList, Qt::UserRole);
340 QList<QStandardItem*> rowItems;
341 rowItems << sysItem << entriesItem << varItem << runItem;
342 experimentItem->appendRow(rowItems);
347 sqlite3_finalize(stmt);
350int DBSelectView::getGeometryCount(
const std::string& system,
const std::string& variation,
int run)
const {
351 if (!db) {
return 0; }
354 std::string query =
"SELECT COUNT(*) FROM geometry WHERE experiment = ? AND system = ? AND variation = ? AND run = ?";
356 sqlite3_stmt* stmt =
nullptr;
357 if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt,
nullptr) == SQLITE_OK) {
358 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
359 sqlite3_bind_text(stmt, 2, system.c_str(), -1, SQLITE_TRANSIENT);
360 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
361 sqlite3_bind_int(stmt, 4, run);
363 if (sqlite3_step(stmt) == SQLITE_ROW) {
364 count = sqlite3_column_int(stmt, 0);
371 sqlite3_finalize(stmt);
375QStringList DBSelectView::getAvailableRuns(
const std::string& system,
const std::string& variation)
const {
377 if (!db) {
return runList; }
379 sqlite3_stmt* stmt =
nullptr;
380 const char* sql_query =
"SELECT DISTINCT run FROM geometry WHERE experiment = ? AND system = ? AND variation = ? ORDER BY run";
382 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
383 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
384 sqlite3_bind_text(stmt, 2, system.c_str(), -1, SQLITE_TRANSIENT);
385 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
387 while (sqlite3_step(stmt) == SQLITE_ROW) {
388 int runVal = sqlite3_column_int(stmt, 0);
389 runList << QString::number(runVal);
393 sqlite3_finalize(stmt);
397bool DBSelectView::systemAvailable(
const std::string& system,
const std::string& variation,
int run) {
398 if (!db) {
return false; }
400 std::string query =
"SELECT COUNT(*) FROM geometry WHERE system = ? AND variation = ? AND run = ?";
401 sqlite3_stmt* stmt =
nullptr;
403 if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt,
nullptr) != SQLITE_OK) {
407 sqlite3_bind_text(stmt, 1, system.c_str(), -1, SQLITE_TRANSIENT);
408 sqlite3_bind_text(stmt, 2, variation.c_str(), -1, SQLITE_TRANSIENT);
409 sqlite3_bind_int(stmt, 3, run);
411 bool available =
false;
412 if (sqlite3_step(stmt) == SQLITE_ROW) {
413 int count = sqlite3_column_int(stmt, 0);
414 available = (count > 0);
417 sqlite3_finalize(stmt);
421QIcon DBSelectView::createStatusIcon(
const QColor& color) {
422 QPixmap pixmap(12, 12);
424 return QIcon(pixmap);
427void DBSelectView::updateSystemItemAppearance(QStandardItem* systemItem) {
428 QStandardItem* parentItem = systemItem->parent();
433 int row = systemItem->row();
434 QStandardItem* varItem = parentItem->child(row, 2);
435 QStandardItem* runItem = parentItem->child(row, 3);
437 QString varStr = varItem ? varItem->data(Qt::EditRole).toString() :
"";
438 QString runStr = runItem ? runItem->data(Qt::EditRole).toString() :
"";
440 int run = runStr.toInt();
441 QString expStr = parentItem->text();
444 experiment = expStr.toStdString();
446 std::string systemName = systemItem->text().toStdString();
447 std::string variation = varStr.toStdString();
449 int count = getGeometryCount(systemName, variation, run);
452 QStandardItem* entriesItem = parentItem->child(row, 1);
454 entriesItem->setText(QString::number(count));
458 bool available = (count > 0);
459 QColor statusColor = available ? QColor(
"green") : QColor(
"red");
460 systemItem->setIcon(createStatusIcon(statusColor));
463 systemItem->setData(QVariant(), Qt::BackgroundRole);
464 systemItem->setData(QVariant(), Qt::ForegroundRole);
467void DBSelectView::updateExperimentHeader() {
468 QStandardItem* selectedExp =
nullptr;
471 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
472 QStandardItem* expItem = experimentModel->item(i, 0);
473 if (expItem && expItem->checkState() == Qt::Checked) {
474 selectedExp = expItem;
480 int totalSystems = selectedExp->rowCount();
481 experimentHeaderLabel->setText(QString(
"Total systems for experiment \"%1\": %2")
482 .arg(selectedExp->text()).arg(totalSystems));
485 experimentHeaderLabel->setText(
"");
489 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
492void DBSelectView::onItemChanged(QStandardItem* item) {
493 if (m_ignoreItemChange || !item)
497 m_ignoreItemChange =
true;
500 if (!item->parent()) {
501 if (item->checkState() == Qt::Checked) {
503 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
504 QStandardItem* expItem = experimentModel->item(i, 0);
506 expItem->setCheckState(Qt::Unchecked);
508 updateExperimentHeader();
512 for (
int i = 0; i < item->rowCount(); ++i) {
513 QStandardItem* sysItem = item->child(i, 0);
515 sysItem->setCheckState(Qt::Unchecked);
517 updateExperimentHeader();
522 if (item->column() == 0) {
523 if (item->checkState() == Qt::Checked) {
524 for (
int i = 0; i < item->parent()->rowCount(); ++i) {
525 QStandardItem* sibling = item->parent()->child(i, 0);
526 if (sibling && sibling != item && sibling->text() == item->text()) {
527 sibling->setCheckState(Qt::Unchecked);
531 updateSystemItemAppearance(item);
533 else if (item->column() == 2 || item->column() == 3) {
534 QStandardItem* sysItem = item->parent()->child(item->row(), 0);
535 updateSystemItemAppearance(sysItem);
539 m_ignoreItemChange =
false;
552 for (
int i = 0; i < experimentModel->rowCount(); i++) {
553 QStandardItem* expItem = experimentModel->item(i, 0);
557 for (
int j = 0; j < expItem->rowCount(); j++) {
558 QStandardItem* sysItem = expItem->child(j, 0);
559 QStandardItem* varItem = expItem->child(j, 2);
560 QStandardItem* runItem = expItem->child(j, 3);
562 if (!sysItem || !varItem || !runItem)
565 if (sysItem->checkState() == Qt::Checked) {
566 std::string systemName = sysItem->text().toStdString();
567 std::string variation = varItem->data(Qt::EditRole).toString().toStdString();
568 int run = runItem->data(Qt::EditRole).toInt();
569 std::string expName = expItem->text().toStdString();
571 log->info(2,
SFUNCTION_NAME,
": adding systemName: ", systemName,
" , variation: ", variation,
", for run:",
run);
573 updatedSystems.emplace_back(
574 std::make_shared<GSystem>(
587 return updatedSystems;
590void DBSelectView::updateModifiedUI() {
592 updateExperimentHeader();
595 titleLabel->setText(
"Experiment Selection* (modified)");
597 titleLabel->setText(
"Experiment Selection");
599 reloadButton->setEnabled(modified);
602 resizeExperimentColumns();
605void DBSelectView::resizeExperimentColumns() {
606 if (!experimentTree) {
return; }
608 experimentTree->resizeColumnToContents(0);
609 experimentTree->setColumnWidth(1, 100);
610 experimentTree->setColumnWidth(2, 150);
611 experimentTree->setColumnWidth(3, 150);
612 experimentTree->header()->setStretchLastSection(
false);
613 experimentTree->expandAll();
621 if (!reloaded_system.empty()) {
622 std::ostringstream gsystemYaml;
624 for (
size_t i = 0; i < reloaded_system.size(); ++i) {
625 const auto& gsys = reloaded_system[i];
626 if (i > 0) { gsystemYaml <<
", "; }
627 gsystemYaml <<
"{name: " << gsys->getName()
628 <<
", factory: " << gsys->getFactoryName()
629 <<
", variation: " << gsys->getVariation();
630 if (gsys->getAnnotations()) {
631 gsystemYaml <<
", annotations: " << *gsys->getAnnotations();
636 gopt->setOptionValueFromString(
"gsystem", gsystemYaml.str());
637 gopt->setOptionValueFromString(
"runno", std::to_string(reloaded_system.front()->getRunno()));
639 for (
auto& gsys : reloaded_system) {
646 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)