Commit 7456bcdc authored by Huff, Israel's avatar Huff, Israel
Browse files

- removed APTEnsWidget and APTEnsDataModel from the example (not relevant to...

- removed APTEnsWidget and APTEnsDataModel from the example (not relevant to it and previously moved to another project)
- more changes to support SFTPFile and SFTPDir usage
- added functionality to GUI to demonstrate SFTPFile and SFTPDir usage (although the latter is not yet working)
parent ca863a5c
Pipeline #100125 failed with stages
in 5 minutes and 11 seconds
......@@ -306,11 +306,13 @@ ssize_t SFTPFile::tell()
class SFTPDirImpl
{
public:
sftp_dir dir = nullptr;
sftp_dir dir = nullptr;
sftp_session sftp = nullptr;
explicit SFTPDirImpl(sftp_session sftpSession, QString dirName)
{
dir = sftp_opendir(sftpSession, dirName.toStdString().c_str());
dir = sftp_opendir(sftpSession, dirName.toStdString().c_str());
sftp = sftpSession;
}
~SFTPDirImpl()
......@@ -338,7 +340,7 @@ bool SFTPDir::hasNext() { return (0 == sftp_dir_eof(p->dir)); }
// caller is responsible for deleting returned SFTPAttributes structure
SFTPAttributes* SFTPDir::next()
{
sftp_attributes sftpResult = sftp_readdir(p->dir->sftp, p->dir);
sftp_attributes sftpResult = sftp_readdir(p->sftp, p->dir);
if (nullptr == sftpResult) return nullptr;
SFTPAttributes* retVal = new SFTPAttributes;
......@@ -376,7 +378,8 @@ bool SFTPDir::close()
int rc = sftp_closedir(p->dir);
if (SSH_NO_ERROR == rc)
{
p->dir = nullptr;
p->dir = nullptr;
p->sftp = nullptr;
return true;
}
return false;
......
......@@ -127,6 +127,8 @@ QByteArray SessionController::readExecOutput()
return p->worker->readExecOutput();
}
Session* SessionController::session() { return p->worker->session(); }
void SessionController::setHost(QString host) { emit setHostRequested(host); }
void SessionController::setLogVerbosity(SessionVerbosity level)
......
......@@ -4,6 +4,7 @@
#include <QObject>
#include "rsmcore/declspec.hh"
#include "rsmcore/session.hh"
#include "rsmcore/sessionlog.hh"
#include <memory>
......@@ -30,6 +31,7 @@ class RSM_PUBLIC SessionController : public QObject
~SessionController();
QByteArray readExecOutput();
Session* session();
public slots:
......
#include "rsmcore/sessionworker.hh"
#include "radixbug/bug.hh"
#include "rsmcore/session.hh"
#include <cassert>
#include <sstream>
......@@ -16,8 +15,8 @@ class SessionWorkerImpl
private:
// privatize copy of this object to prevent their use.
// would be nice to use c++14 '= delete' but c'est la vie.
SessionWorkerImpl(const SessionWorkerImpl& orig) { void(sizeof(orig)); };
void operator=(const SessionWorkerImpl& orig) { void(sizeof(orig)); };
SessionWorkerImpl(const SessionWorkerImpl& orig) { void(sizeof(orig)); }
void operator=(const SessionWorkerImpl& orig) { void(sizeof(orig)); }
public:
Session* session = nullptr;
......@@ -61,6 +60,8 @@ SessionWorker::~SessionWorker()
QByteArray SessionWorker::readExecOutput() { return p->output_buffer; }
Session* SessionWorker::session() { return p->session; }
void SessionWorker::setHost(QString host) { p->session->setHost(host); }
void SessionWorker::setLogVerbosity(SessionVerbosity level)
{
......
......@@ -4,6 +4,7 @@
#include <QObject>
#include "rsmcore/declspec.hh"
#include "rsmcore/session.hh"
#include "rsmcore/sessionlog.hh"
namespace rsm
......@@ -30,6 +31,7 @@ class RSM_PUBLIC SessionWorker : public QObject
SessionWorker(QString host, QObject* parent = nullptr);
~SessionWorker();
QByteArray readExecOutput();
Session* session();
public slots:
......
SET(PORTAL_HEADERS
rsmportalexample.hh
aptenswidget.hh
aptensdatamodel.hh
settings.hh
)
QT5_WRAP_CPP(PORTAL_MOC_FILES
......@@ -13,8 +11,6 @@ TRIBITS_ADD_EXECUTABLE(rsmportalexample
NOEXEPREFIX
SOURCES
rsmportalexample.cc
aptensdatamodel.cc
aptenswidget.cc
settings.cc
${PORTAL_MOC_FILES} ${PORTAL_HEADERS}
)
#include "aptensdatamodel.hh"
using namespace AptEns;
const QMap<Sampling, QString> APTEnsDataModel::sampleStrings = {
{Sampling::uniform, "uniform"}, {Sampling::gaussian, "gaussian"}};
const QMap<SoilType, QString> APTEnsDataModel::soilTypeStrings = {
{SoilType::siliceous, "siliceous"}, {SoilType::calcareous, "calcareous"}};
const QMap<FissionID, QString> APTEnsDataModel::fissionIDStrings = {
{FissionID::u235fi, "u235fi"}, {FissionID::u238fi, "u238fi"},
{FissionID::pu239fi, "pu239fi"}, {FissionID::u235he, "u235he"},
{FissionID::u238he, "u238he"}, {FissionID::pu239he, "pu239he"}};
#ifndef RSM_RSMWIDGETSEXAMPLE_APTENSDATAMODEL_HH_
#define RSM_RSMWIDGETSEXAMPLE_APTENSDATAMODEL_HH_
#include <QMap>
#include <QString>
#include <QStringList>
namespace AptEns
{
enum class Sampling
{
uniform,
gaussian
};
enum class SoilType
{
siliceous,
calcareous
};
enum class FissionID
{
u235fi,
u238fi,
pu239fi,
u235he,
u238he,
pu239he
};
class APTEnsDataModel
{
private:
double mLatitude = 0.;
double mLongitude = 0.;
double mAltitude = 0.;
SoilType mSoilType = SoilType::siliceous;
FissionID mFissionID = FissionID::u235fi;
double mTotalYield = 0.;
bool mYieldDefEnsemble = false;
unsigned mNumYields = 0;
double mMinYield = 0.;
double mMaxYield = 0.;
Sampling mYieldSampling = Sampling::uniform;
double mHeightOfBurst = 0.;
bool mHOBDefEnsemble = false;
unsigned mNumHOBs = 0;
double mMinHOB = 0.;
double mMaxHOB = 0.;
Sampling mHOBSampling = Sampling::uniform;
QStringList mMetFileSets;
QStringList mSetupCfgs;
unsigned mTurbEnsNum = 0;
unsigned mMetEnsNum = 0;
public:
// map strings to enum items
static const QMap<Sampling, QString> sampleStrings;
static const QMap<SoilType, QString> soilTypeStrings;
static const QMap<FissionID, QString> fissionIDStrings;
// returns double value on success or 0. on failure
static double textFieldToDouble(const QString &str)
{
if (str.trimmed().isEmpty()) return 0.;
bool ok;
double value = str.toDouble(&ok);
if (ok) return value;
return 0.;
}
static QString doubleToTextField(double value)
{
return QString::number(value);
}
// returns unsigned value on success or 0 on failure
static unsigned textFieldToUnsigned(const QString &str)
{
if (str.trimmed().isEmpty()) return 0;
bool ok;
int value = str.toInt(&ok);
if (ok && value >= 0) return static_cast<unsigned>(value);
return 0;
}
// getters
double latitude() { return mLatitude; }
double longitude() { return mLongitude; }
double altitude() { return mAltitude; }
SoilType soilType() { return mSoilType; }
FissionID fissionID() { return mFissionID; }
double totalYield() { return mTotalYield; }
bool yieldDefEnsemble() { return mYieldDefEnsemble; }
unsigned numYields() { return mNumYields; }
double minYield() { return mMinYield; }
double maxYield() { return mMaxYield; }
Sampling yieldSampling() { return mYieldSampling; }
double heightOfBurst() { return mHeightOfBurst; }
bool hobDefEnsemble() { return mHOBDefEnsemble; }
unsigned numHOBs() { return mNumHOBs; }
double minHOB() { return mMinHOB; }
double maxHOB() { return mMaxHOB; }
Sampling hobSampling() { return mHOBSampling; }
QStringList &metFileSets() { return mMetFileSets; }
QStringList &setupCfgs() { return mSetupCfgs; }
unsigned turbEnsNum() { return mTurbEnsNum; }
unsigned metEnsNum() { return mMetEnsNum; }
// setters
void setLatitude(double a) { mLatitude = a; }
void setLongitude(double a) { mLongitude = a; }
void setAltitude(double a) { mAltitude = a; }
void setSoilType(SoilType a) { mSoilType = a; }
void setFissionID(FissionID a) { mFissionID = a; }
void setTotalYield(double a) { mTotalYield = a; }
void setYieldDefEnsemble(bool a) { mYieldDefEnsemble = a; }
void setNumYields(unsigned a) { mNumYields = a; }
void setMinYield(double a) { mMinYield = a; }
void setMaxYield(double a) { mMaxYield = a; }
void setYieldSampling(Sampling a) { mYieldSampling = a; }
void setHeightOfBurst(double a) { mHeightOfBurst = a; }
void setHOBDefEnsemble(bool a) { mHOBDefEnsemble = a; }
void setNumHOBs(unsigned a) { mNumHOBs = a; }
void setMinHOB(double a) { mMinHOB = a; }
void setMaxHOB(double a) { mMaxHOB = a; }
void setHOBSampling(Sampling a) { mHOBSampling = a; }
void setTurbEnsNum(unsigned a) { mTurbEnsNum = a; }
void setMetEnsNum(unsigned a) { mMetEnsNum = a; }
};
} // namespace AptEns
#endif // RSM_RSMWIDGETSEXAMPLE_APTENSDATAMODEL_HH_
#include "aptenswidget.hh"
#include "radixbug/bug.hh"
#include "settings.hh"
#include <QApplication>
#include <QFileDialog>
#include <QFileInfo>
#include <QGridLayout>
#include <QMessageBox>
#include <QTimer>
using namespace AptEns;
// ----------------------------------------------------------------------------
StyledLabel::StyledLabel(const QString &title, QWidget *parent)
: QLabel(title, parent)
{
setAlignment(Qt::AlignRight);
}
// ----------------------------------------------------------------------------
CenterTitleGroupBox::CenterTitleGroupBox(const QString &title, QWidget *parent)
: QGroupBox(title, parent)
{
setStyleSheet(R"foo(
QGroupBox {
padding: 1.4em 0em 0em 0em;
}
QGroupBox::title {
subcontrol-origin: padding;
subcontrol-position: top center;
padding: 0.7em 0em 0em 0em;
}
)foo");
}
// ----------------------------------------------------------------------------
LeftTitleGroupBox::LeftTitleGroupBox(const QString &title, QWidget *parent)
: QGroupBox(title, parent)
{
setStyleSheet(R"foo(
QGroupBox {
padding: 0em 0em 0em 0em;
}
QGroupBox::title {
subcontrol-origin: padding;
subcontrol-position: top left;
padding: 0.7em 0em 0em 0em;
left: 1em;
}
)foo");
}
// ----------------------------------------------------------------------------
LineEditFocusSelectAll::LineEditFocusSelectAll(QWidget *parent)
: QLineEdit(parent)
{
}
LineEditFocusSelectAll::LineEditFocusSelectAll(const QString &text,
QWidget *parent)
: QLineEdit(text, parent)
{
}
void LineEditFocusSelectAll::focusInEvent(QFocusEvent *event)
{
QLineEdit::focusInEvent(event);
QTimer::singleShot(0, this, &QLineEdit::selectAll);
}
// ----------------------------------------------------------------------------
APTEnsWidget::APTEnsWidget(QWidget *parent)
: QWidget(parent)
{
QGridLayout *mainLayout = new QGridLayout(this);
mSelectModelBox = new CenterTitleGroupBox("Ensembles", this);
mCurrentModelBox = new CenterTitleGroupBox("", this);
mainLayout->addWidget(mSelectModelBox, 0, 0);
mainLayout->addWidget(mCurrentModelBox, 0, 1);
QGridLayout *smLayout = new QGridLayout(mSelectModelBox);
smLayout->setSpacing(0);
smLayout->setMargin(0);
mNewModelButton = new QPushButton("New", this);
mModelListWidget = new EditableListWidget(mSelectModelBox);
mNewModelButton->setStyleSheet(
"margin : 0.7em 0 0 0; padding : 0.1em 0.1em 0.1em 0.1em;");
smLayout->addWidget(mNewModelButton, 0, 0);
smLayout->addWidget(mModelListWidget, 1, 0);
QGridLayout *cmLayout = new QGridLayout(mCurrentModelBox);
mEventDefBox = new CenterTitleGroupBox("Event Definition", this);
mYieldEnsBox = new CenterTitleGroupBox("Yield Ensemble", this);
mHeightOfBurstEnsBox =
new CenterTitleGroupBox("Height of Burst Ensemble", this);
mMeteorologyBox = new CenterTitleGroupBox("Meteorology", this);
mDispPhysEnsBox =
new CenterTitleGroupBox("Dispersion Physics Ensemble", this);
mTurbEnsBox = new CenterTitleGroupBox("Turbulence Ensemble", this);
mMetEnsBox = new CenterTitleGroupBox("Meteorology Ensemble", this);
int row = 0;
cmLayout->addWidget(mEventDefBox, row, 0, 1, 2);
cmLayout->addWidget(mYieldEnsBox, ++row, 0);
cmLayout->addWidget(mHeightOfBurstEnsBox, row, 1);
cmLayout->addWidget(mMeteorologyBox, ++row, 0, 1, 2);
cmLayout->addWidget(mDispPhysEnsBox, ++row, 0, 1, 2);
cmLayout->addWidget(mTurbEnsBox, ++row, 0);
cmLayout->addWidget(mMetEnsBox, row, 1);
// build event definition box
QGridLayout *edLayout = new QGridLayout(mEventDefBox);
mLatLabel = new StyledLabel("Latitude", mEventDefBox);
mLonLabel = new StyledLabel("Longitude", mEventDefBox);
mAltLabel = new StyledLabel("Altitude", mEventDefBox);
mSoilTypeLabel = new StyledLabel("Soil Type", mEventDefBox);
mFissionIDLabel = new StyledLabel("Fission ID", mEventDefBox);
mLatLineEdit = new LineEditFocusSelectAll(mEventDefBox);
mLonLineEdit = new LineEditFocusSelectAll(mEventDefBox);
mAltLineEdit = new LineEditFocusSelectAll(mEventDefBox);
mSoilTypeComboBox = new QComboBox(mEventDefBox);
mFissionIDComboBox = new QComboBox(mEventDefBox);
mSoilTypeComboBox->setEditable(true);
mSoilTypeComboBox->setInsertPolicy(QComboBox::NoInsert);
for (auto item : APTEnsDataModel::soilTypeStrings)
{
mSoilTypeComboBox->addItem(item);
}
mFissionIDComboBox->setEditable(true);
mFissionIDComboBox->setInsertPolicy(QComboBox::NoInsert);
for (auto item : APTEnsDataModel::fissionIDStrings)
{
mFissionIDComboBox->addItem(item);
}
row = 0;
edLayout->addWidget(mLatLabel, row, 0);
edLayout->addWidget(mLatLineEdit, row, 1);
edLayout->addWidget(mLonLabel, row, 2);
edLayout->addWidget(mLonLineEdit, row, 3);
edLayout->addWidget(mAltLabel, ++row, 0);
edLayout->addWidget(mAltLineEdit, row, 1);
edLayout->addWidget(mSoilTypeLabel, row, 2);
edLayout->addWidget(mSoilTypeComboBox, row, 3);
edLayout->addWidget(mFissionIDLabel, ++row, 0);
edLayout->addWidget(mFissionIDComboBox, row, 1);
// build yield ensemble box
QGridLayout *yeLayout = new QGridLayout(mYieldEnsBox);
mTotalYieldLabel = new StyledLabel("Total Yield", mYieldEnsBox);
mTotalYieldLineEdit = new LineEditFocusSelectAll(mYieldEnsBox);
mYieldDefineEnsCheckBox = new QCheckBox("Define Ensemble", mYieldEnsBox);
mNumYieldsLabel = new StyledLabel("# of Yields", mYieldEnsBox);
mNumYieldsLineEdit = new LineEditFocusSelectAll(mYieldEnsBox);
mMinYieldLabel = new StyledLabel("Min Yield", mYieldEnsBox);
mMinYieldLineEdit = new LineEditFocusSelectAll(mYieldEnsBox);
mMaxYieldLabel = new StyledLabel("Max Yield", mYieldEnsBox);
mMaxYieldLineEdit = new LineEditFocusSelectAll(mYieldEnsBox);
mYieldSamplingLabel = new StyledLabel("Sampling", mYieldEnsBox);
mYieldSamplingComboBox = new QComboBox(mYieldEnsBox);
for (auto item : APTEnsDataModel::sampleStrings)
{
mYieldSamplingComboBox->addItem(item);
}
row = 0;
yeLayout->addWidget(mTotalYieldLabel, row, 0);
yeLayout->addWidget(mTotalYieldLineEdit, row, 1);
yeLayout->addWidget(mYieldDefineEnsCheckBox, row, 2, 1, 2, Qt::AlignRight);
yeLayout->addWidget(mNumYieldsLabel, ++row, 0);
yeLayout->addWidget(mNumYieldsLineEdit, row, 1);
yeLayout->addWidget(mMinYieldLabel, row, 2);
yeLayout->addWidget(mMinYieldLineEdit, row, 3);
yeLayout->addWidget(mMaxYieldLabel, ++row, 0);
yeLayout->addWidget(mMaxYieldLineEdit, row, 1);
yeLayout->addWidget(mYieldSamplingLabel, row, 2);
yeLayout->addWidget(mYieldSamplingComboBox, row, 3);
// build height of burst ensemble box
QGridLayout *hobLayout = new QGridLayout(mHeightOfBurstEnsBox);
mHOBLabel = new StyledLabel("Height of Burst", mHeightOfBurstEnsBox);
mHOBLineEdit = new LineEditFocusSelectAll(mHeightOfBurstEnsBox);
mHOBDefineEnsCheckBox =
new QCheckBox("Define Ensemble", mHeightOfBurstEnsBox);
mNumHOBsLabel = new StyledLabel("# of HoB", mHeightOfBurstEnsBox);
mNumHOBsLineEdit = new LineEditFocusSelectAll(mHeightOfBurstEnsBox);
mMinHOBLabel = new StyledLabel("Min HoB", mHeightOfBurstEnsBox);
mMinHOBLineEdit = new LineEditFocusSelectAll(mHeightOfBurstEnsBox);
mMaxHOBLabel = new StyledLabel("Max HoB", mHeightOfBurstEnsBox);
mMaxHOBLineEdit = new LineEditFocusSelectAll(mHeightOfBurstEnsBox);
mHOBSamplingLabel = new StyledLabel("Sampling", mHeightOfBurstEnsBox);
mHOBSamplingComboBox = new QComboBox(mHeightOfBurstEnsBox);
for (auto item : APTEnsDataModel::sampleStrings)
{
mHOBSamplingComboBox->addItem(item);
}
row = 0;
hobLayout->addWidget(mHOBLabel, row, 0);
hobLayout->addWidget(mHOBLineEdit, row, 1);
hobLayout->addWidget(mHOBDefineEnsCheckBox, row, 2, 1, 2, Qt::AlignRight);
hobLayout->addWidget(mNumHOBsLabel, ++row, 0);
hobLayout->addWidget(mNumHOBsLineEdit, row, 1);
hobLayout->addWidget(mMinHOBLabel, row, 2);
hobLayout->addWidget(mMinHOBLineEdit, row, 3);
hobLayout->addWidget(mMaxHOBLabel, ++row, 0);
hobLayout->addWidget(mMaxHOBLineEdit, row, 1);
hobLayout->addWidget(mHOBSamplingLabel, row, 2);
hobLayout->addWidget(mHOBSamplingComboBox, row, 3);
// build meteorology box
QGridLayout *metLayout = new QGridLayout(mMeteorologyBox);
mMetFileSetsBox = new LeftTitleGroupBox("File Sets", mMeteorologyBox);
mMetFileSetsListWidget = new QListWidget(mMetFileSetsBox);
QGridLayout *metFSLayout = new QGridLayout(mMetFileSetsBox);
mMetAddSetButton = new QPushButton("Add Set", mMeteorologyBox);
mMetRemoveSetButton = new QPushButton("Remove Set", mMeteorologyBox);
mMetAddButton = new QPushButton("Add", mMeteorologyBox);
mMetRemoveButton = new QPushButton("Remove", mMeteorologyBox);
metFSLayout->addWidget(mMetFileSetsListWidget);
row = 0;
metLayout->addWidget(mMetFileSetsBox, row, 0, 1, 5);
QWidget *metSpacer = new QWidget(mMeteorologyBox);
metSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
metLayout->addWidget(metSpacer, ++row, 0);
metLayout->addWidget(mMetAddSetButton, row, 1);
metLayout->addWidget(mMetRemoveSetButton, row, 2);
metLayout->addWidget(mMetAddButton, row, 3);
metLayout->addWidget(mMetRemoveButton, row, 4);
// dispersion physics ensemble box
QGridLayout *dpeLayout = new QGridLayout(mDispPhysEnsBox);
mSetupCfgsBox = new LeftTitleGroupBox("SETUP.CFG Files", mDispPhysEnsBox);
mSetupCfgsListWidget = new QListWidget(mSetupCfgsBox);
QGridLayout *dpeSFLayout = new QGridLayout(mSetupCfgsBox);
mSetupCfgsImportButton = new QPushButton("Import", mDispPhysEnsBox);
mSetupCfgsRemoveButton = new QPushButton("Remove", mDispPhysEnsBox);
dpeSFLayout->addWidget(mSetupCfgsListWidget);
row = 0;
dpeLayout->addWidget(mSetupCfgsBox, row, 0, 1, 5);
QWidget *dpeSpacer = new QWidget(mDispPhysEnsBox);
dpeSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
dpeLayout->addWidget(dpeSpacer, ++row, 0);
dpeLayout->addWidget(mSetupCfgsImportButton, row, 1);
dpeLayout->addWidget(mSetupCfgsRemoveButton, row, 2);
// turbulence ensemble box
QGridLayout *teLayout = new QGridLayout(mTurbEnsBox);
mTurbEnsNumLabel = new StyledLabel("Number", mTurbEnsBox);
mTurbEnsNumLineEdit = new LineEditFocusSelectAll(mTurbEnsBox);
QWidget *teSpacer = new QWidget(mTurbEnsBox);
teSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
teLayout->addWidget(mTurbEnsNumLabel, 0, 0);
teLayout->addWidget(mTurbEnsNumLineEdit, 0, 1);
teLayout->addWidget(teSpacer, 0, 2);
// meteorology ensemble box
QGridLayout *meLayout = new QGridLayout(mMetEnsBox);
mMetEnsNumLabel = new StyledLabel("Number", mMetEnsBox);
mMetEnsNumLineEdit = new LineEditFocusSelectAll(mMetEnsBox);
QWidget *meSpacer = new QWidget(mMetEnsBox);
meSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
meLayout->addWidget(mMetEnsNumLabel, 0, 0);
meLayout->addWidget(mMetEnsNumLineEdit, 0, 1);
meLayout->addWidget(meSpacer, 0, 2);
// select ensemble connections
connect(mNewModelButton, &QPushButton::clicked, this,
[=]() { addNewEnsemble(); });
connect(mModelListWidget, &QListWidget::clicked, this, [=]() {
mCurrentModel = mModelListWidget->currentItem()->text();
updateGUIFromDataModel();
});
connect(mModelListWidget, &EditableListWidget::itemChanged, this, [=]() {
QString newKey = mModelListWidget->currentItem()->text().trimmed();
if (newKey.length() == 0 || mDataModels.find(newKey) != mDataModels.end())
{
mModelListWidget->currentItem()->setText(mCurrentModel);
return;
}
// write trimmed version back to list view
mModelListWidget->currentItem()->setText(newKey);
mDataModels[newKey] = mDataModels[mCurrentModel];
mDataModels.erase(mDataModels.find(mCurrentModel));
mCurrentModel = newKey;
updateGUIFromDataModel();
});
connect(mModelListWidget, &EditableListWidget::sigEditItem, this, [=]() {
mModelListWidget->editItem(mModelListWidget->currentItem());
});
connect(mModelListWidget, &EditableListWidget::sigRemoveItem, this, [=]() {
// TODO: fix crash
mDataModels.erase(mDataModels.find(mCurrentModel));
int row = mModelListWidget->currentRow();
auto it = mModelListWidget->takeItem(row);
delete it;
if (mDataModels.size() == 0)
{
addNewEnsemble();
return;
}
QString newKey = mModelListWidget->currentItem()->text();
mCurrentModel = newKey;
updateGUIFromDataModel();
});