From 948abe121e7020756765c5e9463dce99de22ee53 Mon Sep 17 00:00:00 2001 From: Tobias Knopff <t.knopff@fz-juelich.de> Date: Wed, 6 Oct 2021 15:16:58 +0200 Subject: [PATCH] Make JobPropertiesWidget a regular widget and not derived from SessionItemWidget --- GUI/Models/JobItem.cpp | 44 +++-- GUI/Models/JobItem.h | 9 +- .../JobWidgets/JobPropertiesTableModel.cpp | 183 ++++++++++++++++++ .../JobWidgets/JobPropertiesTableModel.h | 48 +++++ GUI/Views/JobWidgets/JobPropertiesWidget.cpp | 125 ++++++------ GUI/Views/JobWidgets/JobPropertiesWidget.h | 30 ++- 6 files changed, 350 insertions(+), 89 deletions(-) create mode 100644 GUI/Views/JobWidgets/JobPropertiesTableModel.cpp create mode 100644 GUI/Views/JobWidgets/JobPropertiesTableModel.h diff --git a/GUI/Models/JobItem.cpp b/GUI/Models/JobItem.cpp index 524c2d8083a..985203c3c2c 100644 --- a/GUI/Models/JobItem.cpp +++ b/GUI/Models/JobItem.cpp @@ -41,10 +41,6 @@ JobItem::JobItem() : SessionItem(M_TYPE) addProperty(P_BEGIN_TIME, QString())->setEditable(false); addProperty(P_END_TIME, QString())->setEditable(false); - auto durationItem = addProperty(P_DURATION, QString()); - durationItem->setEditable(false); - durationItem->setToolTip("Duration of DWBA simulation in sec.msec format"); - addProperty(P_COMMENTS, QString())->setVisible(false); addProperty(P_PROGRESS, 0)->setVisible(false); addProperty(P_PRESENTATION_TYPE, QVariant::Type::Invalid)->setVisible(false); @@ -162,6 +158,11 @@ void JobItem::setBeginTime(const QDateTime& begin_time) setItemValue(P_BEGIN_TIME, begin_time.toString(Qt::ISODateWithMs)); } +bool JobItem::isBeginTimePropertyName(const QString& name) +{ + return name == P_BEGIN_TIME; +} + QDateTime JobItem::endTime() const { return QDateTime::fromString(getItemValue(P_END_TIME).toString(), Qt::ISODateWithMs); @@ -170,16 +171,21 @@ QDateTime JobItem::endTime() const void JobItem::setEndTime(const QDateTime& end_time) { setItemValue(P_END_TIME, end_time.toString(Qt::ISODateWithMs)); - QString duration; - if (end_time.isValid()) { - QDateTime begin_time = beginTime(); - if (begin_time.isValid()) { - qint64 delta = begin_time.msecsTo(end_time); - if (delta >0) - duration = QString::number(delta / 1000., 'f', 3); - } - } - setItemValue(P_DURATION, duration); +} + +bool JobItem::isEndTimePropertyName(const QString& name) +{ + return name == P_END_TIME; +} + +std::optional<size_t> JobItem::duration() const +{ + QDateTime begin_time = beginTime(); + QDateTime end_time = endTime(); + if (begin_time.isValid() && end_time.isValid() && begin_time < end_time) + return begin_time.msecsTo(end_time); + else + return std::nullopt; } QString JobItem::getComments() const @@ -333,11 +339,21 @@ Data1DViewItem* JobItem::createDataViewItem() return model()->insertItem<Data1DViewItem>(this, -1, T_DATAVIEW); } +QString JobItem::sampleName() const +{ + return getItemValue(P_SAMPLE_NAME).toString(); +} + void JobItem::setSampleName(const QString& name) { getItem(P_SAMPLE_NAME)->setValue(name); } +QString JobItem::instrumentName() const +{ + return getItemValue(P_INSTRUMENT_NAME).toString(); +} + void JobItem::setInstrumentName(const QString& name) { getItem(P_INSTRUMENT_NAME)->setValue(name); diff --git a/GUI/Models/JobItem.h b/GUI/Models/JobItem.h index 22506f34906..9536c3b6f5e 100644 --- a/GUI/Models/JobItem.h +++ b/GUI/Models/JobItem.h @@ -33,6 +33,7 @@ class ISimulation; class SimulationOptionsItem; #include <QDateTime> +#include <optional> class BA_CORE_API_ JobItem : public SessionItem { @@ -44,7 +45,6 @@ private: static constexpr auto P_STATUS{"Status"}; static constexpr auto P_BEGIN_TIME{"Begin time"}; static constexpr auto P_END_TIME{"End time"}; - static constexpr auto P_DURATION{"Duration"}; static constexpr auto P_COMMENTS{"Comments"}; static constexpr auto P_PROGRESS{"Progress"}; static constexpr auto P_PRESENTATION_TYPE{"Presentation type"}; @@ -84,9 +84,14 @@ public: QDateTime beginTime() const; void setBeginTime(const QDateTime& begin_time); + static bool isBeginTimePropertyName(const QString& name); QDateTime endTime() const; void setEndTime(const QDateTime& end_time); + static bool isEndTimePropertyName(const QString& name); + + /// if begin and end time are both available the duration in ms, otherwise empty + std::optional<size_t> duration() const; QString getComments() const; void setComments(const QString& comments); @@ -130,8 +135,10 @@ public: void addDataViewItem(Data1DViewItem* data_view); Data1DViewItem* createDataViewItem(); + QString sampleName() const; void setSampleName(const QString& name); + QString instrumentName() const; void setInstrumentName(const QString& name); const QString presentationType() const; diff --git a/GUI/Views/JobWidgets/JobPropertiesTableModel.cpp b/GUI/Views/JobWidgets/JobPropertiesTableModel.cpp new file mode 100644 index 00000000000..ac4ab1b73c5 --- /dev/null +++ b/GUI/Views/JobWidgets/JobPropertiesTableModel.cpp @@ -0,0 +1,183 @@ +// ************************************************************************************************ +// +// BornAgain: simulate and fit reflection and scattering +// +//! @file GUI/Views/JobWidgets/JobPropertiesTableModel.cpp +//! @brief Implements class JobPropertiesTableModel +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************************************ + +#include "GUI/Views/JobWidgets/JobPropertiesTableModel.h" +#include "GUI/Models/JobItem.h" + +namespace { +namespace Column { +enum Columns { Name, Value }; +} +const QString ColumnNames[] = {"Name", "Value"}; +const int NumColumns = std::size(ColumnNames); + +namespace Row { +enum Rows { Name, Sample, Instrument, Status, Begin, End, Duration }; +} +const QString RowNames[] = {"Name", "Sample", "Instrument", "Status", "Begin", "End", "Duration"}; +const int NumRows = std::size(RowNames); + +const QString ModelDateShortFormat = "yyyy.MM.dd hh:mm:ss"; +} // namespace + +//================================================================================================== +// JobPropertiesTableModel +//================================================================================================== + +//-------------------------------------------------------------------------------------------------- +// public member functions +//-------------------------------------------------------------------------------------------------- + +JobPropertiesTableModel::JobPropertiesTableModel(QObject* parent) + : QAbstractTableModel(parent), m_item(nullptr) +{ +} + +JobPropertiesTableModel::~JobPropertiesTableModel() +{ + if (m_item) + m_item->mapper()->unsubscribe(this); +} + +int JobPropertiesTableModel::rowCount(const QModelIndex& parent) const +{ + if (!parent.isValid() && m_item) + return NumRows; + else + return 0; +} + +int JobPropertiesTableModel::columnCount(const QModelIndex& parent) const +{ + if (!parent.isValid() && m_item) + return NumColumns; + else + return 0; +} + +QVariant JobPropertiesTableModel::data(const QModelIndex& index, int role) const +{ + if ((role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::ToolTipRole) + || index.column() < 0 || index.column() >= NumColumns || index.row() < 0 + || index.row() >= NumRows || !m_item) + return QVariant(); + + switch (index.column()) { + case Column::Name: + return RowNames[index.row()]; + break; + case Column::Value: { + switch (index.row()) { + case Row::Name: + return m_item->itemName(); + break; + case Row::Sample: + return m_item->sampleName(); + break; + case Row::Instrument: + return m_item->instrumentName(); + break; + case Row::Status: + return m_item->getStatus(); + break; + case Row::Begin: + if (role == Qt::ToolTipRole) + return m_item->beginTime().toString(Qt::DefaultLocaleLongDate); + else + return m_item->beginTime().toString(ModelDateShortFormat); + break; + case Row::End: + if (role == Qt::ToolTipRole) + return m_item->endTime().toString(Qt::DefaultLocaleLongDate); + else + return m_item->endTime().toString(ModelDateShortFormat); + break; + case Row::Duration: { + std::optional<size_t> duration = m_item->duration(); + if (duration) + return QString("%1 s").arg(duration.value() / 1000., 0, 'f', 3); + else + return QVariant(); + } break; + default: + return QVariant(); + } + } + default: + return QVariant(); + } +} + +QVariant JobPropertiesTableModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section >= 0 + && section < NumColumns) + return ColumnNames[section]; + else + return QVariant(); +} + +Qt::ItemFlags JobPropertiesTableModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags f = QAbstractTableModel::flags(index); + if (index.column() == Column::Value && index.row() == Row::Name && m_item) + f.setFlag(Qt::ItemIsEditable); + return f; +} + +bool JobPropertiesTableModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (role != Qt::EditRole || index.column() != Column::Value || index.row() != Row::Name + || !m_item) + return false; + m_item->setItemName(value.toString()); + emit dataChanged(index, index, {role}); + return true; +} + +void JobPropertiesTableModel::setItem(JobItem* item) +{ + beginResetModel(); + if (m_item) + m_item->mapper()->unsubscribe(this); + m_item = item; + if (m_item) + m_item->mapper()->setOnPropertyChange( + std::bind(&JobPropertiesTableModel::notifyJobPropertyChange, this, + std::placeholders::_1), + this); + endResetModel(); +} + +//-------------------------------------------------------------------------------------------------- +// private member functions +//-------------------------------------------------------------------------------------------------- + +void JobPropertiesTableModel::notifyJobPropertyChange(const QString& property) +{ + if (SessionItem::isItemNamePropertyName(property)) + emit dataChanged(index(Row::Name, Column::Value), index(Row::Name, Column::Value), + {Qt::DisplayRole, Qt::EditRole}); + else if (JobItem::isStatusPropertyName(property)) + emit dataChanged(index(Row::Status, Column::Value), index(Row::Status, Column::Value), + {Qt::DisplayRole, Qt::EditRole}); + else if (JobItem::isBeginTimePropertyName(property)) + emit dataChanged(index(Row::Begin, Column::Value), index(Row::Begin, Column::Value), + {Qt::DisplayRole, Qt::EditRole}); + else if (JobItem::isEndTimePropertyName(property)) + // will change duration very probably too + emit dataChanged(index(Row::End, Column::Value), index(Row::Duration, Column::Value), + {Qt::DisplayRole, Qt::EditRole}); +} diff --git a/GUI/Views/JobWidgets/JobPropertiesTableModel.h b/GUI/Views/JobWidgets/JobPropertiesTableModel.h new file mode 100644 index 00000000000..92481115eec --- /dev/null +++ b/GUI/Views/JobWidgets/JobPropertiesTableModel.h @@ -0,0 +1,48 @@ +// ************************************************************************************************ +// +// BornAgain: simulate and fit reflection and scattering +// +//! @file GUI/Views/JobWidgets/JobPropertiesWidget.h +//! @brief Defines class JobPropertiesWidget +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************************************ + +#ifndef BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESTABLEMODEL_H +#define BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESTABLEMODE_H + +#include <QAbstractTableModel> + +class JobItem; + + +//! The JobPropertiesTableModel is a table model for the properties of a job except for the comment. +//! The name of the job is editable, all other fields are read only. + +class JobPropertiesTableModel : public QAbstractTableModel { + Q_OBJECT +public: + explicit JobPropertiesTableModel(QObject* parent = nullptr); + ~JobPropertiesTableModel(); + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, + int role = Qt::EditRole) override; + void setItem(JobItem* item); + +private: + void notifyJobPropertyChange(const QString& property); + +private: + JobItem* m_item; +}; + +#endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESTABLEMODEL_H diff --git a/GUI/Views/JobWidgets/JobPropertiesWidget.cpp b/GUI/Views/JobWidgets/JobPropertiesWidget.cpp index a8ae841465f..00552914d67 100644 --- a/GUI/Views/JobWidgets/JobPropertiesWidget.cpp +++ b/GUI/Views/JobWidgets/JobPropertiesWidget.cpp @@ -15,97 +15,106 @@ #include "GUI/Views/JobWidgets/JobPropertiesWidget.h" #include "GUI/Models/JobItem.h" #include "GUI/Views/CommonWidgets/StyleUtils.h" -#include "GUI/Views/PropertyEditor/ComponentEditor.h" +#include "GUI/Views/JobWidgets/JobPropertiesTableModel.h" #include "GUI/Views/Tools/mainwindow_constants.h" #include <QTabBar> #include <QTabWidget> #include <QTextEdit> +#include <QTreeView> #include <QVBoxLayout> -JobPropertiesWidget::JobPropertiesWidget(QWidget* parent) - : SessionItemWidget(parent) - , m_tabWidget(new QTabWidget) - , m_componentEditor(new ComponentEditor) - , m_commentsEditor(new QTextEdit) - , m_block_update(false) +namespace { +enum ETabId { JOB_PROPERTIES, JOB_COMMENTS }; +} + +//================================================================================================== +// JobPropertiesWidget +//================================================================================================== + +//-------------------------------------------------------------------------------------------------- +// public member functions +//-------------------------------------------------------------------------------------------------- + +JobPropertiesWidget::JobPropertiesWidget(QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f), m_item(nullptr) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); setWindowTitle(GUI::Constants::JobPropertiesWidgetName); - auto mainLayout = new QVBoxLayout; - mainLayout->setMargin(0); - mainLayout->setSpacing(0); - mainLayout->setContentsMargins(0, 0, 0, 0); + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + m_tabWidget = new QTabWidget(this); m_tabWidget->setTabPosition(QTabWidget::South); - m_tabWidget->insertTab(JOB_PROPERTIES, m_componentEditor, "Job Properties"); - m_tabWidget->insertTab(JOB_COMMENTS, m_commentsEditor, "Details"); + layout->addWidget(m_tabWidget); - mainLayout->addWidget(m_tabWidget); - setLayout(mainLayout); + m_propertiesView = new QTreeView(this); + m_propertiesView->setRootIsDecorated(false); + m_propertiesView->setAlternatingRowColors(true); + m_tabWidget->insertTab(JOB_PROPERTIES, m_propertiesView, "Job Properties"); + m_propertiesModel = new JobPropertiesTableModel(this); + m_propertiesView->setModel(m_propertiesModel); - connect(m_commentsEditor, &QTextEdit::textChanged, this, &JobPropertiesWidget::onTextChanged); + m_commentsEditor = new QTextEdit(this); + m_tabWidget->insertTab(JOB_COMMENTS, m_commentsEditor, "Comments"); + + connect(m_commentsEditor, &QTextEdit::textChanged, this, + &JobPropertiesWidget::onCommentsEdited); } -QSize JobPropertiesWidget::sizeHint() const +JobPropertiesWidget::~JobPropertiesWidget() { - return QSize(GUI::Utils::Style::PropertyPanelWidth(), GUI::Utils::Style::PropertyPanelWidth()); + if (m_item) + m_item->mapper()->unsubscribe(this); } -QSize JobPropertiesWidget::minimumSizeHint() const +QSize JobPropertiesWidget::sizeHint() const { return QSize(GUI::Utils::Style::PropertyPanelWidth(), GUI::Utils::Style::PropertyPanelWidth()); } -void JobPropertiesWidget::subscribeToItem() +QSize JobPropertiesWidget::minimumSizeHint() const { - currentItem()->mapper()->setOnPropertyChange( - [this](const QString& name) { - if (JobItem::isCommentsPropertyName(name)) - updateItem(); - }, - this); - - m_componentEditor->setItem(currentItem()); - - updateItem(); + return QSize(GUI::Utils::Style::PropertyPanelWidth(), GUI::Utils::Style::PropertyPanelWidth()); } -void JobPropertiesWidget::unsubscribeFromItem() +void JobPropertiesWidget::setItem(JobItem* item) { - m_componentEditor->setItem(nullptr); + if (m_item) + m_item->mapper()->unsubscribe(this); + m_item = item; + m_propertiesModel->setItem(m_item); + if (m_item) { + if (m_item->isFailed()) + m_tabWidget->tabBar()->setTabTextColor(JOB_COMMENTS, Qt::red); + else + m_tabWidget->tabBar()->setTabTextColor(JOB_COMMENTS, Qt::black); + m_commentsEditor->setText(m_item->getComments()); + m_item->mapper()->setOnPropertyChange( + std::bind(&JobPropertiesWidget::notifyJobPropertyChange, this, std::placeholders::_1)); + } else + m_commentsEditor->clear(); } -void JobPropertiesWidget::contextMenuEvent(QContextMenuEvent*) -{ - // Reimplemented to suppress menu from main window -} +//-------------------------------------------------------------------------------------------------- +// private member functions +//-------------------------------------------------------------------------------------------------- -void JobPropertiesWidget::onTextChanged() +void JobPropertiesWidget::notifyJobPropertyChange(const QString& property) { - m_block_update = true; - JobItem* item = jobItem(); - if (item) - item->setComments(m_commentsEditor->toPlainText()); - m_block_update = false; + if (JobItem::isCommentsPropertyName(property) && m_item + && m_item->getComments() != m_commentsEditor->toPlainText()) + m_commentsEditor->setPlainText(m_item->getComments()); } -void JobPropertiesWidget::updateItem() -{ - if (m_block_update) - return; - - if (JobItem* item = jobItem()) { - if (item->getStatus() == "Failed") - m_tabWidget->tabBar()->setTabTextColor(JOB_COMMENTS, Qt::red); - else - m_tabWidget->tabBar()->setTabTextColor(JOB_COMMENTS, Qt::black); - - m_commentsEditor->setText(item->getComments()); - } -} +//-------------------------------------------------------------------------------------------------- +// private slots +//-------------------------------------------------------------------------------------------------- -JobItem* JobPropertiesWidget::jobItem() +void JobPropertiesWidget::onCommentsEdited() { - return dynamic_cast<JobItem*>(currentItem()); + if (m_item) + m_item->setComments(m_commentsEditor->toPlainText()); } diff --git a/GUI/Views/JobWidgets/JobPropertiesWidget.h b/GUI/Views/JobWidgets/JobPropertiesWidget.h index 47ddd1a3e1e..cad504f2edc 100644 --- a/GUI/Views/JobWidgets/JobPropertiesWidget.h +++ b/GUI/Views/JobWidgets/JobPropertiesWidget.h @@ -15,41 +15,39 @@ #ifndef BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESWIDGET_H #define BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESWIDGET_H -#include "GUI/Views/CommonWidgets/SessionItemWidget.h" +#include <QWidget> class JobItem; -class QTextEdit; +class JobPropertiesTableModel; class QTabWidget; -class ComponentEditor; +class QTextEdit; +class QTreeView; //! The JobPropertiesWidget class holds component editor for JobItem. Part of JobSelectorWidget, //! resides at lower left corner of JobView. -class JobPropertiesWidget : public SessionItemWidget { +class JobPropertiesWidget : public QWidget { Q_OBJECT public: - enum ETabId { JOB_PROPERTIES, JOB_COMMENTS }; - explicit JobPropertiesWidget(QWidget* parent = nullptr); + explicit JobPropertiesWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + ~JobPropertiesWidget(); + void setItem(JobItem* item); QSize sizeHint() const; QSize minimumSizeHint() const; -protected: - void subscribeToItem(); - void unsubscribeFromItem(); - void contextMenuEvent(QContextMenuEvent*); +private: + void notifyJobPropertyChange(const QString& property); private slots: - void onTextChanged(); + void onCommentsEdited(); private: - void updateItem(); - JobItem* jobItem(); - QTabWidget* m_tabWidget; - ComponentEditor* m_componentEditor; + QTreeView* m_propertiesView; + JobPropertiesTableModel* m_propertiesModel; QTextEdit* m_commentsEditor; - bool m_block_update; + JobItem* m_item; }; #endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESWIDGET_H -- GitLab