From a3003043b12ee55f21838b222e83f47c3e703848 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Fri, 31 May 2024 16:37:13 +0100 Subject: [PATCH 01/42] Starting to implement add/remove buttons for variable removal. --- .../expressionVariableVector.cpp | 16 +++- .../keywordWidgets/expressionVariableVector.h | 5 +- .../expressionVariableVector.ui | 81 ++++++++++++++++++- .../models/expressionVariableVectorModel.cpp | 25 +++++- .../models/expressionVariableVectorModel.h | 6 +- src/keywords/expressionVariableVector.cpp | 1 + src/keywords/expressionVariableVector.h | 1 + src/procedure/nodes/node.cpp | 11 ++- src/procedure/nodes/node.h | 3 +- 9 files changed, 138 insertions(+), 11 deletions(-) diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index e969d41d97..97c8a056c9 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -20,7 +20,9 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWi // Connect signals / slots connect(&variableModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QVector &)), this, - SLOT(modelDataChanged(const QModelIndex &, const QModelIndex &))); + SLOT(variableDataChanged(const QModelIndex &, const QModelIndex &))); + connect(ui_.VariablesTable->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(variableSelectionChanged(const QItemSelection &, const QItemSelection &))); // Add suitable delegate to the table ui_.VariablesTable->setItemDelegateForColumn(2, new ExponentialSpinDelegate(this)); @@ -31,7 +33,7 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWi */ // Variable data changed -void ExpressionVariableVectorKeywordWidget::modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +void ExpressionVariableVectorKeywordWidget::variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (refreshing_) return; @@ -39,5 +41,15 @@ void ExpressionVariableVectorKeywordWidget::modelDataChanged(const QModelIndex & Q_EMIT(keywordDataChanged(keyword_->editSignals())); } +void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItemSelection ¤t, + const QItemSelection &previous) +{ + ui_.RemoveVariableButton->setEnabled(current.empty()); +} + +void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) { } + +void ExpressionVariableVectorKeywordWidget::ui_RemoveVariableButton_clicked(bool checked) {} + // Update value displayed in widget void ExpressionVariableVectorKeywordWidget::updateValue(const Flags &mutationFlags) {} diff --git a/src/gui/keywordWidgets/expressionVariableVector.h b/src/gui/keywordWidgets/expressionVariableVector.h index b3ddf50573..d0fd1c62d5 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.h +++ b/src/gui/keywordWidgets/expressionVariableVector.h @@ -36,7 +36,10 @@ class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidg ExpressionVariableVectorModel variableModel_; private Q_SLOTS: - void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void variableSelectionChanged(const QItemSelection ¤t, const QItemSelection &previous); + void ui_AddVariableButton_clicked(bool checked); + void ui_RemoveVariableButton_clicked(bool checked); Q_SIGNALS: // Keyword data changed diff --git a/src/gui/keywordWidgets/expressionVariableVector.ui b/src/gui/keywordWidgets/expressionVariableVector.ui index ada6448ab3..97ff965b02 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.ui +++ b/src/gui/keywordWidgets/expressionVariableVector.ui @@ -7,7 +7,7 @@ 0 0 194 - 81 + 99 @@ -53,8 +53,85 @@ + + + + 4 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + 0 + 0 + + + + Remove + + + + :/general/icons/remove.svg:/general/icons/remove.svg + + + + 16 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Add + + + + :/general/icons/add.svg:/general/icons/add.svg + + + + 16 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + + + - + + + diff --git a/src/gui/models/expressionVariableVectorModel.cpp b/src/gui/models/expressionVariableVectorModel.cpp index 744870077a..6a5a6ec5d0 100644 --- a/src/gui/models/expressionVariableVectorModel.cpp +++ b/src/gui/models/expressionVariableVectorModel.cpp @@ -6,7 +6,7 @@ // Set source variable data void ExpressionVariableVectorModel::setData(std::vector> &variables, - const ProcedureNode *parentNode) + ProcedureNode *parentNode) { beginResetModel(); variables_ = variables; @@ -123,3 +123,26 @@ bool ExpressionVariableVectorModel::setData(const QModelIndex &index, const QVar Q_EMIT dataChanged(index, index); return true; } + +bool ExpressionVariableVectorModel::insertRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(count); + beginInsertRows(parent, row, row); + parentNode_->addParameter("NewParameter", 0.0, row); + endInsertRows(); + return true; +} + +bool ExpressionVariableVectorModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(count); + if (row >= rowCount(parent) || row < 0) + { + return false; + } + + beginRemoveRows(parent, row, row); +// ranges_->get().erase(ranges_->get().begin() + row); + endRemoveRows(); + return true; +} diff --git a/src/gui/models/expressionVariableVectorModel.h b/src/gui/models/expressionVariableVectorModel.h index c5190a5756..2f63b0bc3c 100644 --- a/src/gui/models/expressionVariableVectorModel.h +++ b/src/gui/models/expressionVariableVectorModel.h @@ -21,11 +21,11 @@ class ExpressionVariableVectorModel : public QAbstractTableModel // Source variable data OptionalReferenceWrapper>> variables_; // Parent procedure node (to enable parameter search) - const ProcedureNode *parentNode_{nullptr}; + ProcedureNode *parentNode_{nullptr}; public: // Set source variable data - void setData(std::vector> &variables, const ProcedureNode *parentNode); + void setData(std::vector> &variables, ProcedureNode *parentNode); int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; @@ -33,4 +33,6 @@ class ExpressionVariableVectorModel : public QAbstractTableModel QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; + bool insertRows(int row, int count, const QModelIndex &parent) override; + bool removeRows(int row, int count, const QModelIndex &parent) override; }; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 630d131fe6..b0588f1bdc 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -23,6 +23,7 @@ std::vector> &ExpressionVariableVectorKeywor const std::vector> &ExpressionVariableVectorKeyword::data() const { return data_; } // Return parent ProcedureNode +ProcedureNode *ExpressionVariableVectorKeyword::parentNode() { return parentNode_; } const ProcedureNode *ExpressionVariableVectorKeyword::parentNode() const { return parentNode_; } /* diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index bc7514ac1a..b126b2eb8f 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -33,6 +33,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase std::vector> &data(); const std::vector> &data() const; // Return parent ProcedureNode + ProcedureNode *parentNode(); const ProcedureNode *parentNode() const; /* diff --git a/src/procedure/nodes/node.cpp b/src/procedure/nodes/node.cpp index 0ab6e03f2b..9be05b3d5b 100644 --- a/src/procedure/nodes/node.cpp +++ b/src/procedure/nodes/node.cpp @@ -154,11 +154,18 @@ OptionalReferenceWrapper ProcedureNode::branch() { return */ // Add new parameter -std::shared_ptr ProcedureNode::addParameter(std::string_view name, const ExpressionValue &initialValue) +std::shared_ptr ProcedureNode::addParameter(std::string_view name, const ExpressionValue &initialValue, + std::optional insertAt) { - auto &newVar = parameters_.emplace_back(std::make_shared(name, initialValue)); + auto newVar = std::make_shared(name, initialValue); + if (insertAt) + parameters_.insert(parameters_.begin() + *insertAt, newVar); + else + parameters_.emplace_back(newVar); + if (type_ != ProcedureNode::NodeType::Parameters) newVar->setNamePrefix(name_); + return newVar; } diff --git a/src/procedure/nodes/node.h b/src/procedure/nodes/node.h index bab00d4cf5..02381419d9 100644 --- a/src/procedure/nodes/node.h +++ b/src/procedure/nodes/node.h @@ -139,7 +139,8 @@ class ProcedureNode : public std::enable_shared_from_this, public public: // Add new parameter - std::shared_ptr addParameter(std::string_view name, const ExpressionValue &initialValue = {}); + std::shared_ptr addParameter(std::string_view name, const ExpressionValue &initialValue = {}, + std::optional insertAt = {}); // Return the named parameter (if it exists) std::shared_ptr getParameter(std::string_view name, const std::shared_ptr &excludeParameter = {}); From ac9cb7e52a1b660620d0195a47ce8e7ab5cccbc4 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Sat, 1 Jun 2024 17:18:45 +0100 Subject: [PATCH 02/42] Start sketching out C++-side custom model. --- src/keywords/expressionVariableVector.cpp | 13 +++- src/keywords/expressionVariableVector.h | 86 +++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index b0588f1bdc..84ca02bef2 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -8,9 +8,20 @@ #include "procedure/nodes/node.h" #include +enum ExpressionVariableProperties +{ + Name, + Type, + Value +}; +std::vector expressionVariableProperties = {{ExpressionVariableProperties::Name, "Name", PropertyType::String, false}, + {ExpressionVariableProperties::Type, "Type", PropertyType::String, false}, + {ExpressionVariableProperties::Value, "Value", PropertyType::String, false} +}; + ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &data, ProcedureNode *parentNode) - : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode) + : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_, expressionVariableProperties) { } diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index b126b2eb8f..edbd09b32f 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -10,6 +10,91 @@ class ExpressionVariable; class ProcedureNode; +// Data property types +enum class PropertyType +{ + Invalid, + Integer, + Double, + String +}; + +// Name / Column Title, Data Type, ReadOnly? +using DataItemProperty = std::tuple; + +struct DataItemValue +{ + PropertyType type; + union + { + int intValue{0}; + double doubleValue; + std::string stringValue; + }; +}; +template class DataModel +{ + public: + // Data access functions + using DataSetFunction = std::function; + using DataGetFunction = std::function; + DataModel(std::vector &data, const std::vector &itemProperties) : data_(data), itemProperties_(itemProperties) {} + + private: + // Target data for the model + std::vector &data_; + + /* + * Extent + */ + private: + // Return whether the supplied index is valid + bool isIndexValid(int childIndex, int columnIndex) const { return (childIndex >= 0 && childIndex < data_.size() && columnIndex >= 0); } + // Functions for accessing data extents (table style) + std::function childCountFunction_{ + [&](const int childIndex, const int columnIndex) { return isIndexValid(childIndex, columnIndex) ? 0 : data_.size(); }}; + std::function dataItemCountFunction_{ + [&](const int childIndex, const int columnIndex) { return !isIndexValid(childIndex, columnIndex) ? 0 : itemProperties_.size(); }}; + + public: + // Return number of children (rows) for the specified index + int nChildren(int childIndex, int columnIndex) { return childCountFunction_(childIndex, columnIndex); } + // Return number of data items (columns) for the specified index + int nDataItems(int childIndex, int columnIndex) { return dataItemCountFunction_(childIndex, columnIndex); } + + /* + * Data Access + */ + private: + // Descriptions of relevant item properties within a single object in the container + std::vector itemProperties_; + // Return whether column index holds / can be set by the given type + bool isPropertyType(int columnIndex, PropertyType descriptorType) { return std::get<2>(itemProperties_[columnIndex]) == descriptorType; } + // Set / get functions, unique per class + DataSetFunction setDataFunction_; + DataGetFunction getDataFunction_; + + public: + // Set data functions + bool setData(int childIndex, int columnIndex, const DataItemValue &newValue) { + // Check index validity + if (!isIndexValid(childIndex, columnIndex)) + return false; + + if (!isPropertyType(columnIndex, PropertyType::Integer)) + { + fmt::print("Refusing to set data '{}' with an int since it is of a different type.\n", std::get<1>(itemProperties_[columnIndex])); + return false; + } + + // Set the child at the specified index + if (!setDataFunction_) + return false; + else + return setDataFunction_(data_[childIndex], std::get<0>(itemProperties_[columnIndex]), newValue); + } +}; + // Keyword managing vector of ExpressionVariable class ExpressionVariableVectorKeyword : public KeywordBase { @@ -23,6 +108,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase private: // Reference to vector of data std::vector> &data_; + DataModel> dataModel_; // Parent ProcedureNode ProcedureNode *parentNode_; From 84f684c3aa589676ccde8b474493fb01ee6ee849 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Sun, 2 Jun 2024 00:05:04 +0100 Subject: [PATCH 03/42] All working so far. --- .../expressionVariableVector.cpp | 7 +- .../keywordWidgets/expressionVariableVector.h | 2 +- .../models/expressionVariableVectorModel.cpp | 135 ++++++---------- .../models/expressionVariableVectorModel.h | 22 +-- src/keywords/expressionVariableVector.cpp | 60 ++++++- src/keywords/expressionVariableVector.h | 151 ++++++++++++++---- 6 files changed, 233 insertions(+), 144 deletions(-) diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index 97c8a056c9..4c545e4826 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -9,13 +9,12 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWidget *parent, ExpressionVariableVectorKeyword *keyword, const CoreData &coreData) - : QWidget(parent), KeywordWidgetBase(coreData), keyword_(keyword) + : QWidget(parent), KeywordWidgetBase(coreData), keyword_(keyword), variableModel_(keyword->dataModel()) { // Create and set up the UI for our widget ui_.setupUi(this); - // Set up model - variableModel_.setData(keyword->data(), keyword->parentNode()); + // Set model ui_.VariablesTable->setModel(&variableModel_); // Connect signals / slots @@ -47,7 +46,7 @@ void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItem ui_.RemoveVariableButton->setEnabled(current.empty()); } -void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) { } +void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) {} void ExpressionVariableVectorKeywordWidget::ui_RemoveVariableButton_clicked(bool checked) {} diff --git a/src/gui/keywordWidgets/expressionVariableVector.h b/src/gui/keywordWidgets/expressionVariableVector.h index d0fd1c62d5..b86c766485 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.h +++ b/src/gui/keywordWidgets/expressionVariableVector.h @@ -33,7 +33,7 @@ class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidg // Main form declaration Ui::ExpressionVariableVectorWidget ui_; // Model for table - ExpressionVariableVectorModel variableModel_; + DataTableModelInterface variableModel_; private Q_SLOTS: void variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); diff --git a/src/gui/models/expressionVariableVectorModel.cpp b/src/gui/models/expressionVariableVectorModel.cpp index 6a5a6ec5d0..146737d0c0 100644 --- a/src/gui/models/expressionVariableVectorModel.cpp +++ b/src/gui/models/expressionVariableVectorModel.cpp @@ -4,137 +4,100 @@ #include "gui/models/expressionVariableVectorModel.h" #include "procedure/nodes/node.h" -// Set source variable data -void ExpressionVariableVectorModel::setData(std::vector> &variables, - ProcedureNode *parentNode) -{ - beginResetModel(); - variables_ = variables; - parentNode_ = parentNode; - endResetModel(); -} +DataTableModelInterface::DataTableModelInterface(DataModelBase &dataModel) : dataModel_(dataModel) {} -int ExpressionVariableVectorModel::rowCount(const QModelIndex &parent) const +int DataTableModelInterface::rowCount(const QModelIndex &parent) const { - if (parent.isValid()) - return 0; - return variables_ ? variables_->get().size() : 0; + return dataModel_.nChildren(parent.row(), parent.column()); } -int ExpressionVariableVectorModel::columnCount(const QModelIndex &parent) const + +int DataTableModelInterface::columnCount(const QModelIndex &parent) const { - if (parent.isValid()) - return 0; - return 3; + return dataModel_.nProperties(parent.row(), parent.column()); } -Qt::ItemFlags ExpressionVariableVectorModel::flags(const QModelIndex &index) const +Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const { + // TODO if (index.column() == 1) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; else return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; } -QVariant ExpressionVariableVectorModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant DataTableModelInterface::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole) + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return {}; - if (orientation == Qt::Horizontal) - switch (section) - { - case 0: - return "Name"; - case 1: - return "Type"; - case 2: - return "Value"; - default: - return {}; - } - - return {}; + return QString::fromStdString(dataModel_.propertyName(section)); } // Bond model -QVariant ExpressionVariableVectorModel::data(const QModelIndex &index, int role) const +QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const { - if (!index.isValid() || !variables_) - return {}; - - auto &vars = variables_->get(); - - if (index.row() >= vars.size() || index.row() < 0) - return {}; - if (role != Qt::DisplayRole && role != Qt::EditRole) return {}; - auto &var = vars[index.row()]; + // Get the specified data property + auto property = dataModel_.getProperty(index.row(), index.column()); - switch (index.column()) + switch (property.type()) { - // Name - case 0: - return QString::fromStdString(std::string(var->baseName())); - case 1: - return var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real"; - case 2: - return QString::fromStdString(var->value().asString()); + case (PropertyType::Invalid): + return {}; + case (PropertyType::Integer): + return property.intValue(); + case (PropertyType::Double): + return property.doubleValue(); + case (PropertyType::String): + return QString::fromStdString(property.stringValue()); default: return {}; } - - return {}; } -bool ExpressionVariableVectorModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant &value, int role) { - if (role != Qt::EditRole || !variables_ || index.column() == 1) + if (role != Qt::EditRole || dataModel_.isPropertyFlagSet(index.column(), PropertyFlag::ReadOnly)) return false; - auto &var = variables_->get()[index.row()]; - - if (index.column() == 0) - { - // Name - must check for existing var in scope with the same name - auto p = parentNode_->getParameterInScope(value.toString().toStdString()); - if (p && p != var) - return false; - var->setBaseName(value.toString().toStdString()); - } - else if (index.column() == 2) + // Set new value + bool success = false; + switch (dataModel_.propertyType(index.column())) { - // Value - need to check type (int vs double) - auto varValue = value.toString().toStdString(); - bool isFloatingPoint = false; - if (DissolveSys::isNumber(varValue, isFloatingPoint)) - { - if (isFloatingPoint) - var->setValue(value.toDouble()); - else - var->setValue(value.toInt()); - } - else - return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", varValue, - var->baseName()); + case (PropertyType::Integer): + success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toInt())); + break; + case (PropertyType::Double): + success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toDouble())); + break; + case (PropertyType::String): + success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toString().toStdString())); + break; + default: + break; } - Q_EMIT dataChanged(index, index); - return true; + if (success) + Q_EMIT dataChanged(index, index); + + return success; } -bool ExpressionVariableVectorModel::insertRows(int row, int count, const QModelIndex &parent) +bool DataTableModelInterface::insertRows(int row, int count, const QModelIndex &parent) { + // TODO Q_UNUSED(count); beginInsertRows(parent, row, row); - parentNode_->addParameter("NewParameter", 0.0, row); + // parentNode_->addParameter("NewParameter", 0.0, row); endInsertRows(); return true; } -bool ExpressionVariableVectorModel::removeRows(int row, int count, const QModelIndex &parent) +bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex &parent) { + // TODO Q_UNUSED(count); if (row >= rowCount(parent) || row < 0) { @@ -142,7 +105,7 @@ bool ExpressionVariableVectorModel::removeRows(int row, int count, const QModelI } beginRemoveRows(parent, row, row); -// ranges_->get().erase(ranges_->get().begin() + row); + // ranges_->get().erase(ranges_->get().begin() + row); endRemoveRows(); return true; } diff --git a/src/gui/models/expressionVariableVectorModel.h b/src/gui/models/expressionVariableVectorModel.h index 2f63b0bc3c..4da2f172c3 100644 --- a/src/gui/models/expressionVariableVectorModel.h +++ b/src/gui/models/expressionVariableVectorModel.h @@ -3,29 +3,23 @@ #pragma once -#include "expression/variable.h" +#include "keywords/expressionVariableVector.h" #include "templates/optionalRef.h" #include #include #include -// Forward Declarations -class ProcedureNode; - -// Expression Variable Vector Model -class ExpressionVariableVectorModel : public QAbstractTableModel +// Qt Interface to DataTableModel +class DataTableModelInterface : public QAbstractTableModel { Q_OBJECT - private: - // Source variable data - OptionalReferenceWrapper>> variables_; - // Parent procedure node (to enable parameter search) - ProcedureNode *parentNode_{nullptr}; - public: - // Set source variable data - void setData(std::vector> &variables, ProcedureNode *parentNode); + DataTableModelInterface(DataModelBase &dataModel); + + private: + // Model with which to interface + DataModelBase &dataModel_; int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 84ca02bef2..2cecefcb65 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -14,15 +14,64 @@ enum ExpressionVariableProperties Type, Value }; -std::vector expressionVariableProperties = {{ExpressionVariableProperties::Name, "Name", PropertyType::String, false}, - {ExpressionVariableProperties::Type, "Type", PropertyType::String, false}, - {ExpressionVariableProperties::Value, "Value", PropertyType::String, false} -}; +std::vector expressionVariableProperties = { + {ExpressionVariableProperties::Name, "Name", PropertyType::String, {}}, + {ExpressionVariableProperties::Type, "Type", PropertyType::String, {PropertyFlag::ReadOnly}}, + {ExpressionVariableProperties::Value, "Value", PropertyType::String, {}}}; ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &data, ProcedureNode *parentNode) : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_, expressionVariableProperties) { + dataModel_.setPropertyFunctions( + [&](const std::shared_ptr &var, int propertyIndex) + { + switch (propertyIndex) + { + case (ExpressionVariableProperties::Name): + return DataItemValue(var->baseName()); + case (ExpressionVariableProperties::Type): + return DataItemValue(std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); + case (ExpressionVariableProperties::Value): + return DataItemValue(var->value().asString()); + default: + return DataItemValue(); + } + }, + [&](std::shared_ptr &var, int propertyIndex, const DataItemValue &newValue) + { + switch (propertyIndex) + { + case (ExpressionVariableProperties::Name): + { + // Must check for existing var in scope with the same name + auto p = parentNode_->getParameter(newValue.stringValue()); + if (p && p != var) + return false; + var->setBaseName(newValue.stringValue()); + } + break; + case (ExpressionVariableProperties::Value): + { + // Value - need to check type (int vs double) + bool isFloatingPoint = false; + if (DissolveSys::isNumber(newValue.stringValue(), isFloatingPoint)) + { + if (isFloatingPoint) + var->setValue(std::stod(newValue.stringValue())); + else + var->setValue(std::stoi(newValue.stringValue())); + } + else + return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", + newValue.stringValue(), var->baseName()); + } + break; + default: + return false; + } + return true; + }); } /* @@ -33,6 +82,9 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &ExpressionVariableVectorKeyword::data() { return data_; } const std::vector> &ExpressionVariableVectorKeyword::data() const { return data_; } +// Return data model +DataTableModel> &ExpressionVariableVectorKeyword::dataModel() { return dataModel_; } + // Return parent ProcedureNode ProcedureNode *ExpressionVariableVectorKeyword::parentNode() { return parentNode_; } const ProcedureNode *ExpressionVariableVectorKeyword::parentNode() const { return parentNode_; } diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index edbd09b32f..ad1b646da1 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -19,26 +19,85 @@ enum class PropertyType String }; -// Name / Column Title, Data Type, ReadOnly? -using DataItemProperty = std::tuple; +// Data property flags +enum PropertyFlag +{ + ReadOnly +}; + +// Index / Column ID, Name / Column Title, Data Type, ReadOnly? +using DataItemProperty = std::tuple>; + +class DataItemValue +{ + public: + DataItemValue() : type_(PropertyType::Invalid) {} + DataItemValue(int i) : type_(PropertyType::Integer), intValue_(i) {} + DataItemValue(double d) : type_(PropertyType::Double), doubleValue_(d) {} + DataItemValue(std::string_view sv) : type_(PropertyType::String), stringValue_(sv) {} + DataItemValue(std::string s) : type_(PropertyType::String), stringValue_(std::move(s)) {} + + private: + PropertyType type_; + int intValue_{0}; + double doubleValue_{0.0}; + std::string stringValue_; + + public: + // Return value type + PropertyType type() const { return type_; } + // Return integer value + int intValue() const { return intValue_; } + // Return double value + double doubleValue() const { return doubleValue_; } + // Return string value + std::string stringValue() const { return stringValue_; } +}; -struct DataItemValue +class DataModelBase { - PropertyType type; - union + public: + DataModelBase(const std::vector &itemProperties) : itemProperties_(itemProperties) {} + + /* + * Properties + */ + protected: + // Descriptions of relevant item properties within a single object in the container + std::vector itemProperties_; + + public: + // Return name of specified property + std::string propertyName(int propertyIndex) { return std::get<1>(itemProperties_[propertyIndex]); } + // Return property type for the specified column + PropertyType propertyType(int propertyIndex) { return std::get<2>(itemProperties_[propertyIndex]); } + // Return whether the specified property flag is set + bool isPropertyFlagSet(int propertyIndex, PropertyFlag flag) { - int intValue{0}; - double doubleValue; - std::string stringValue; - }; + return std::get<3>(itemProperties_[propertyIndex]).isSet(flag); + } + + public: + // Return number of children (rows) for the specified index + virtual int nChildren(int dataIndex, int propertyIndex) = 0; + // Return number of properties (i.e. columns) for the specified index + virtual int nProperties(int dataIndex, int propertyIndex) = 0; + // Set property function + virtual bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) = 0; + // Get property function + virtual DataItemValue getProperty(int dataIndex, int propertyIndex) = 0; }; -template class DataModel + +template class DataTableModel : public DataModelBase { public: // Data access functions - using DataSetFunction = std::function; - using DataGetFunction = std::function; - DataModel(std::vector &data, const std::vector &itemProperties) : data_(data), itemProperties_(itemProperties) {} + using PropertySetFunction = std::function; + using PropertyGetFunction = std::function; + DataTableModel(std::vector &data, const std::vector &itemProperties) + : DataModelBase(itemProperties), data_(data) + { + } private: // Target data for the model @@ -49,49 +108,69 @@ template class DataModel */ private: // Return whether the supplied index is valid - bool isIndexValid(int childIndex, int columnIndex) const { return (childIndex >= 0 && childIndex < data_.size() && columnIndex >= 0); } + bool isIndexValid(int dataIndex, int propertyIndex) const + { + return dataIndex >= 0 && dataIndex < data_.size() && propertyIndex >= 0 && propertyIndex < itemProperties_.size(); + } // Functions for accessing data extents (table style) - std::function childCountFunction_{ - [&](const int childIndex, const int columnIndex) { return isIndexValid(childIndex, columnIndex) ? 0 : data_.size(); }}; - std::function dataItemCountFunction_{ - [&](const int childIndex, const int columnIndex) { return !isIndexValid(childIndex, columnIndex) ? 0 : itemProperties_.size(); }}; + std::function childCountFunction_{[&](const int dataIndex, const int propertyIndex) { + return isIndexValid(dataIndex, propertyIndex) ? 0 : data_.size(); + }}; + std::function propertyCountFunction_{[&](const int dataIndex, const int propertyIndex) + { return itemProperties_.size(); }}; public: // Return number of children (rows) for the specified index - int nChildren(int childIndex, int columnIndex) { return childCountFunction_(childIndex, columnIndex); } - // Return number of data items (columns) for the specified index - int nDataItems(int childIndex, int columnIndex) { return dataItemCountFunction_(childIndex, columnIndex); } + int nChildren(int dataIndex, int propertyIndex) final { return childCountFunction_(dataIndex, propertyIndex); } + // Return number of properties per child (i.e. columns) for the specified index + int nProperties(int dataIndex, int propertyIndex) final { return propertyCountFunction_(dataIndex, propertyIndex); } /* * Data Access */ private: - // Descriptions of relevant item properties within a single object in the container - std::vector itemProperties_; - // Return whether column index holds / can be set by the given type - bool isPropertyType(int columnIndex, PropertyType descriptorType) { return std::get<2>(itemProperties_[columnIndex]) == descriptorType; } // Set / get functions, unique per class - DataSetFunction setDataFunction_; - DataGetFunction getDataFunction_; + PropertySetFunction setPropertyFunction_; + PropertyGetFunction getPropertyFunction_; public: - // Set data functions - bool setData(int childIndex, int columnIndex, const DataItemValue &newValue) { + // Set property access functions + void setPropertyFunctions(PropertyGetFunction getFunction, PropertySetFunction setFunction) + { + getPropertyFunction_ = std::move(getFunction); + setPropertyFunction_ = std::move(setFunction); + } + // Set property + bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) final + { // Check index validity - if (!isIndexValid(childIndex, columnIndex)) + if (!isIndexValid(dataIndex, propertyIndex)) return false; - if (!isPropertyType(columnIndex, PropertyType::Integer)) + if (std::get<3>(itemProperties_[propertyIndex]).isSet(PropertyFlag::ReadOnly)) { - fmt::print("Refusing to set data '{}' with an int since it is of a different type.\n", std::get<1>(itemProperties_[columnIndex])); + fmt::print("Refusing to set data '{}' since it is read-only.\n", std::get<1>(itemProperties_[propertyIndex])); return false; } // Set the child at the specified index - if (!setDataFunction_) + if (!setPropertyFunction_) return false; else - return setDataFunction_(data_[childIndex], std::get<0>(itemProperties_[columnIndex]), newValue); + return setPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex]), newValue); + } + // Get property + DataItemValue getProperty(int dataIndex, int propertyIndex) final + { + // Check index validity + if (!isIndexValid(dataIndex, propertyIndex)) + return {}; + + // Set the child at the specified index + if (!getPropertyFunction_) + return {}; + else + return getPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex])); } }; @@ -108,7 +187,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase private: // Reference to vector of data std::vector> &data_; - DataModel> dataModel_; + DataTableModel> dataModel_; // Parent ProcedureNode ProcedureNode *parentNode_; @@ -118,6 +197,8 @@ class ExpressionVariableVectorKeyword : public KeywordBase // Return reference to vector of data std::vector> &data(); const std::vector> &data() const; + // Return data model + DataTableModel> &dataModel(); // Return parent ProcedureNode ProcedureNode *parentNode(); const ProcedureNode *parentNode() const; From 540b3411fac3ef7b3cc7178620290db47c8fa532 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 9 Jul 2024 12:57:34 +0100 Subject: [PATCH 04/42] Move to using std::variant. --- .../models/expressionVariableVectorModel.cpp | 26 ++++------ src/keywords/expressionVariableVector.cpp | 27 ++++++----- src/keywords/expressionVariableVector.h | 48 +++++++++---------- 3 files changed, 48 insertions(+), 53 deletions(-) diff --git a/src/gui/models/expressionVariableVectorModel.cpp b/src/gui/models/expressionVariableVectorModel.cpp index 146737d0c0..5da28fd365 100644 --- a/src/gui/models/expressionVariableVectorModel.cpp +++ b/src/gui/models/expressionVariableVectorModel.cpp @@ -33,7 +33,6 @@ QVariant DataTableModelInterface::headerData(int section, Qt::Orientation orient return QString::fromStdString(dataModel_.propertyName(section)); } -// Bond model QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const { if (role != Qt::DisplayRole && role != Qt::EditRole) @@ -42,19 +41,11 @@ QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const // Get the specified data property auto property = dataModel_.getProperty(index.row(), index.column()); - switch (property.type()) - { - case (PropertyType::Invalid): - return {}; - case (PropertyType::Integer): - return property.intValue(); - case (PropertyType::Double): - return property.doubleValue(); - case (PropertyType::String): - return QString::fromStdString(property.stringValue()); - default: - return {}; - } + // Construct a QVariant from the contents of our std::variant + return std::visit(DataItemVisitor{[](int arg) { return QVariant(arg); }, [](double arg) { return QVariant(arg); }, + [](std::string_view arg) { return QVariant(QString::fromStdString(std::string(arg))); }, + [](std::string &arg) { return QVariant(QString::fromStdString(arg)); }}, + property); } bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant &value, int role) @@ -67,13 +58,14 @@ bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant & switch (dataModel_.propertyType(index.column())) { case (PropertyType::Integer): - success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toInt())); + success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::DataItemValue(value.toInt())); break; case (PropertyType::Double): - success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toDouble())); + success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::DataItemValue(value.toDouble())); break; case (PropertyType::String): - success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toString().toStdString())); + success = dataModel_.setProperty(index.row(), index.column(), + DataModelBase::DataItemValue(value.toString().toStdString())); break; default: break; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 2cecefcb65..b8583b0c35 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -29,42 +29,45 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vectorbaseName()); + return DataModelBase::DataItemValue(var->baseName()); case (ExpressionVariableProperties::Type): - return DataItemValue(std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); + return DataModelBase::DataItemValue( + std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); case (ExpressionVariableProperties::Value): - return DataItemValue(var->value().asString()); + return DataModelBase::DataItemValue(var->value().asString()); default: - return DataItemValue(); + return DataModelBase::DataItemValue(); } }, - [&](std::shared_ptr &var, int propertyIndex, const DataItemValue &newValue) + [&](std::shared_ptr &var, int propertyIndex, const DataModelBase::DataItemValue &newValue) { switch (propertyIndex) { case (ExpressionVariableProperties::Name): { // Must check for existing var in scope with the same name - auto p = parentNode_->getParameter(newValue.stringValue()); + auto newName = DataModelBase::asString(newValue); + auto p = parentNode_->getParameter(newName); if (p && p != var) return false; - var->setBaseName(newValue.stringValue()); + var->setBaseName(newName); } break; case (ExpressionVariableProperties::Value): { // Value - need to check type (int vs double) bool isFloatingPoint = false; - if (DissolveSys::isNumber(newValue.stringValue(), isFloatingPoint)) + auto value = DataModelBase::asString(newValue); + if (DissolveSys::isNumber(value, isFloatingPoint)) { if (isFloatingPoint) - var->setValue(std::stod(newValue.stringValue())); + var->setValue(std::stod(value)); else - var->setValue(std::stoi(newValue.stringValue())); + var->setValue(std::stoi(value)); } else - return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", - newValue.stringValue(), var->baseName()); + return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", value, + var->baseName()); } break; default: diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index ad1b646da1..fe39737cca 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -5,6 +5,7 @@ #include "expression/node.h" #include "keywords/base.h" +#include // Forward Declarations class ExpressionVariable; @@ -13,7 +14,6 @@ class ProcedureNode; // Data property types enum class PropertyType { - Invalid, Integer, Double, String @@ -28,37 +28,37 @@ enum PropertyFlag // Index / Column ID, Name / Column Title, Data Type, ReadOnly? using DataItemProperty = std::tuple>; -class DataItemValue +// Helper type for DataItemValue visitor +template struct DataItemVisitor : Ts... { - public: - DataItemValue() : type_(PropertyType::Invalid) {} - DataItemValue(int i) : type_(PropertyType::Integer), intValue_(i) {} - DataItemValue(double d) : type_(PropertyType::Double), doubleValue_(d) {} - DataItemValue(std::string_view sv) : type_(PropertyType::String), stringValue_(sv) {} - DataItemValue(std::string s) : type_(PropertyType::String), stringValue_(std::move(s)) {} - - private: - PropertyType type_; - int intValue_{0}; - double doubleValue_{0.0}; - std::string stringValue_; - - public: - // Return value type - PropertyType type() const { return type_; } - // Return integer value - int intValue() const { return intValue_; } - // Return double value - double doubleValue() const { return doubleValue_; } - // Return string value - std::string stringValue() const { return stringValue_; } + using Ts::operator()...; }; +// Explicit deduction guide for DataItemValue visitor +template DataItemVisitor(Ts...) -> DataItemVisitor; class DataModelBase { public: DataModelBase(const std::vector &itemProperties) : itemProperties_(itemProperties) {} + /* + * Value Handling + */ + public: + using DataItemValue = std::variant; + // Return value as string + static std::string asString(const DataItemValue &value) + { + if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else + return std::get(value); + } + /* * Properties */ From 85c271f14fbfafd1d9f8b0c477cde7561384a9b0 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 9 Jul 2024 13:10:22 +0100 Subject: [PATCH 05/42] Simplify table extent determination. --- .../models/expressionVariableVectorModel.cpp | 10 ++-------- src/keywords/expressionVariableVector.h | 20 +++++++------------ 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/gui/models/expressionVariableVectorModel.cpp b/src/gui/models/expressionVariableVectorModel.cpp index 5da28fd365..81fde83fb2 100644 --- a/src/gui/models/expressionVariableVectorModel.cpp +++ b/src/gui/models/expressionVariableVectorModel.cpp @@ -6,15 +6,9 @@ DataTableModelInterface::DataTableModelInterface(DataModelBase &dataModel) : dataModel_(dataModel) {} -int DataTableModelInterface::rowCount(const QModelIndex &parent) const -{ - return dataModel_.nChildren(parent.row(), parent.column()); -} +int DataTableModelInterface::rowCount(const QModelIndex &parent) const { return dataModel_.nDataItems(); } -int DataTableModelInterface::columnCount(const QModelIndex &parent) const -{ - return dataModel_.nProperties(parent.row(), parent.column()); -} +int DataTableModelInterface::columnCount(const QModelIndex &parent) const { return dataModel_.nProperties(); } Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const { diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index fe39737cca..c5ab6a7c6d 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -78,10 +78,10 @@ class DataModelBase } public: - // Return number of children (rows) for the specified index - virtual int nChildren(int dataIndex, int propertyIndex) = 0; + // Return number of data items (i.e. rows) for the specified index + virtual int nDataItems() = 0; // Return number of properties (i.e. columns) for the specified index - virtual int nProperties(int dataIndex, int propertyIndex) = 0; + virtual int nProperties() = 0; // Set property function virtual bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) = 0; // Get property function @@ -112,18 +112,12 @@ template class DataTableModel : public DataModelBase { return dataIndex >= 0 && dataIndex < data_.size() && propertyIndex >= 0 && propertyIndex < itemProperties_.size(); } - // Functions for accessing data extents (table style) - std::function childCountFunction_{[&](const int dataIndex, const int propertyIndex) { - return isIndexValid(dataIndex, propertyIndex) ? 0 : data_.size(); - }}; - std::function propertyCountFunction_{[&](const int dataIndex, const int propertyIndex) - { return itemProperties_.size(); }}; public: - // Return number of children (rows) for the specified index - int nChildren(int dataIndex, int propertyIndex) final { return childCountFunction_(dataIndex, propertyIndex); } - // Return number of properties per child (i.e. columns) for the specified index - int nProperties(int dataIndex, int propertyIndex) final { return propertyCountFunction_(dataIndex, propertyIndex); } + // Return number of data items (rows) in the table + int nDataItems() final { return data_.size(); } + // Return number of properties per data item (i.e. columns) in the table + int nProperties() final { return itemProperties_.size(); } /* * Data Access From e17edf4b21117f6acc22b59d063f3d45d3bf0882 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 9 Jul 2024 13:13:39 +0100 Subject: [PATCH 06/42] Error message. --- src/gui/models/expressionVariableVectorModel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/models/expressionVariableVectorModel.cpp b/src/gui/models/expressionVariableVectorModel.cpp index 81fde83fb2..98adff62c5 100644 --- a/src/gui/models/expressionVariableVectorModel.cpp +++ b/src/gui/models/expressionVariableVectorModel.cpp @@ -62,6 +62,7 @@ bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant & DataModelBase::DataItemValue(value.toString().toStdString())); break; default: + Messenger::error("DataTableModelInterface doesn't know how to handle this PropertyType.\n"); break; } From df2310f9ca5a9fa496bb7fbca854b07d6c631e9e Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 9 Jul 2024 13:28:02 +0100 Subject: [PATCH 07/42] Move to new files. --- src/gui/CMakeLists.txt | 2 + ...rModel.cpp => dataTableModelInterface.cpp} | 2 +- ...ectorModel.h => dataTableModelInterface.h} | 0 .../keywordWidgets/expressionVariableVector.h | 2 +- src/gui/models/CMakeLists.txt | 2 - src/templates/dataModel.h | 169 ++++++++++++++++++ 6 files changed, 173 insertions(+), 4 deletions(-) rename src/gui/{models/expressionVariableVectorModel.cpp => dataTableModelInterface.cpp} (98%) rename src/gui/{models/expressionVariableVectorModel.h => dataTableModelInterface.h} (100%) create mode 100644 src/templates/dataModel.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index b556e6b65f..e005bc817e 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -19,6 +19,8 @@ add_library( configurationViewerWidget.cpp configurationViewerWidget.h configurationViewerWidget.ui + dataTableModelInterface.cpp + dataTableModelInterface.h dataViewer.cpp dataViewer_input.cpp dataViewer_interaction.cpp diff --git a/src/gui/models/expressionVariableVectorModel.cpp b/src/gui/dataTableModelInterface.cpp similarity index 98% rename from src/gui/models/expressionVariableVectorModel.cpp rename to src/gui/dataTableModelInterface.cpp index 98adff62c5..e94ca404b2 100644 --- a/src/gui/models/expressionVariableVectorModel.cpp +++ b/src/gui/dataTableModelInterface.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors -#include "gui/models/expressionVariableVectorModel.h" +#include "gui/dataTableModelInterface.h" #include "procedure/nodes/node.h" DataTableModelInterface::DataTableModelInterface(DataModelBase &dataModel) : dataModel_(dataModel) {} diff --git a/src/gui/models/expressionVariableVectorModel.h b/src/gui/dataTableModelInterface.h similarity index 100% rename from src/gui/models/expressionVariableVectorModel.h rename to src/gui/dataTableModelInterface.h diff --git a/src/gui/keywordWidgets/expressionVariableVector.h b/src/gui/keywordWidgets/expressionVariableVector.h index b86c766485..3e9bf83b49 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.h +++ b/src/gui/keywordWidgets/expressionVariableVector.h @@ -5,7 +5,7 @@ #include "gui/keywordWidgets/base.h" #include "gui/keywordWidgets/ui_expressionVariableVector.h" -#include "gui/models/expressionVariableVectorModel.h" +#include "gui/dataTableModelInterface.h" #include "keywords/expressionVariableVector.h" // Forward Declarations diff --git a/src/gui/models/CMakeLists.txt b/src/gui/models/CMakeLists.txt index 118e761b6c..5ff05b4eda 100644 --- a/src/gui/models/CMakeLists.txt +++ b/src/gui/models/CMakeLists.txt @@ -68,7 +68,6 @@ set(models_SRCS dissolveModelImageProvider.cpp dissolveModelImageProvider.h enumOptionsModel.cpp - expressionVariableVectorModel.cpp externalPotentialModel.cpp ffSortFilterModel.cpp forcefieldModel.cpp @@ -124,7 +123,6 @@ qt6_wrap_cpp( braggReflectionModel.h dataManagerSimulationModel.h enumOptionsModel.h - expressionVariableVectorModel.h forcefieldModel.h isotopologueSetModel.h pairPotentialModel.h diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h new file mode 100644 index 0000000000..8a46ae4481 --- /dev/null +++ b/src/templates/dataModel.h @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "templates/flags.h" +#include +#include + +// Data property types +enum class PropertyType +{ + Integer, + Double, + String +}; + +// Data property flags +enum PropertyFlag +{ + ReadOnly +}; + +// Index / Column ID, Name / Column Title, Data Type, ReadOnly? +using DataItemProperty = std::tuple>; + +// Helper type for DataItemValue visitor +template struct DataItemVisitor : Ts... +{ + using Ts::operator()...; +}; +// Explicit deduction guide for DataItemValue visitor +template DataItemVisitor(Ts...) -> DataItemVisitor; + +/* + * DataModelBase - An abstract class intended to allow our custom C++ data classes to be accessed in a consistent + * and simplified way through Qt's Model-View classes. + */ +class DataModelBase +{ + public: + DataModelBase(const std::vector &itemProperties) : itemProperties_(itemProperties) {} + + /* + * Value Handling + */ + public: + using DataItemValue = std::variant; + // Return value as string + static std::string asString(const DataItemValue &value) + { + if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else + return std::get(value); + } + + /* + * Properties + */ + protected: + // Descriptions of relevant item properties within a single object in the container + std::vector itemProperties_; + + public: + // Return name of specified property + std::string propertyName(int propertyIndex) { return std::get<1>(itemProperties_[propertyIndex]); } + // Return property type for the specified column + PropertyType propertyType(int propertyIndex) { return std::get<2>(itemProperties_[propertyIndex]); } + // Return whether the specified property flag is set + bool isPropertyFlagSet(int propertyIndex, PropertyFlag flag) + { + return std::get<3>(itemProperties_[propertyIndex]).isSet(flag); + } + + public: + // Return number of data items (i.e. rows) for the specified index + virtual int nDataItems() = 0; + // Return number of properties (i.e. columns) for the specified index + virtual int nProperties() = 0; + // Set property function + virtual bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) = 0; + // Get property function + virtual DataItemValue getProperty(int dataIndex, int propertyIndex) = 0; +}; + +template class DataTableModel : public DataModelBase +{ + public: + // Data access functions + using PropertySetFunction = std::function; + using PropertyGetFunction = std::function; + DataTableModel(std::vector &data, const std::vector &itemProperties) + : DataModelBase(itemProperties), data_(data) + { + } + + private: + // Target data for the model + std::vector &data_; + + /* + * Extent + */ + private: + // Return whether the supplied index is valid + bool isIndexValid(int dataIndex, int propertyIndex) const + { + return dataIndex >= 0 && dataIndex < data_.size() && propertyIndex >= 0 && propertyIndex < itemProperties_.size(); + } + + public: + // Return number of data items (rows) in the table + int nDataItems() final { return data_.size(); } + // Return number of properties per data item (i.e. columns) in the table + int nProperties() final { return itemProperties_.size(); } + + /* + * Data Access + */ + private: + // Set / get functions, unique per class + PropertySetFunction setPropertyFunction_; + PropertyGetFunction getPropertyFunction_; + + public: + // Set property access functions + void setPropertyFunctions(PropertyGetFunction getFunction, PropertySetFunction setFunction) + { + getPropertyFunction_ = std::move(getFunction); + setPropertyFunction_ = std::move(setFunction); + } + // Set property + bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) final + { + // Check index validity + if (!isIndexValid(dataIndex, propertyIndex)) + return false; + + if (std::get<3>(itemProperties_[propertyIndex]).isSet(PropertyFlag::ReadOnly)) + { + fmt::print("Refusing to set data '{}' since it is read-only.\n", std::get<1>(itemProperties_[propertyIndex])); + return false; + } + + // Set the child at the specified index + if (!setPropertyFunction_) + return false; + else + return setPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex]), newValue); + } + // Get property + DataItemValue getProperty(int dataIndex, int propertyIndex) final + { + // Check index validity + if (!isIndexValid(dataIndex, propertyIndex)) + return {}; + + // Set the child at the specified index + if (!getPropertyFunction_) + return {}; + else + return getPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex])); + } +}; From 7e8e7de611a1b691309d8ccc4cd7592815240e13 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Wed, 10 Jul 2024 21:55:11 +0100 Subject: [PATCH 08/42] Comments. --- src/gui/dataTableModelInterface.cpp | 27 +++++++++++++++++++-------- src/gui/dataTableModelInterface.h | 14 +++++++++++++- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/gui/dataTableModelInterface.cpp b/src/gui/dataTableModelInterface.cpp index e94ca404b2..042c26f42f 100644 --- a/src/gui/dataTableModelInterface.cpp +++ b/src/gui/dataTableModelInterface.cpp @@ -6,10 +6,17 @@ DataTableModelInterface::DataTableModelInterface(DataModelBase &dataModel) : dataModel_(dataModel) {} +/* + * QAbstractTableModel Overrides + */ + +// Return row count int DataTableModelInterface::rowCount(const QModelIndex &parent) const { return dataModel_.nDataItems(); } +// Return column count int DataTableModelInterface::columnCount(const QModelIndex &parent) const { return dataModel_.nProperties(); } +// Return flags for the specified model index Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const { // TODO @@ -19,6 +26,7 @@ Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; } +// Return header data for the specified section, orientation, and role QVariant DataTableModelInterface::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) @@ -27,6 +35,7 @@ QVariant DataTableModelInterface::headerData(int section, Qt::Orientation orient return QString::fromStdString(dataModel_.propertyName(section)); } +// Return data for the index and role specified QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const { if (role != Qt::DisplayRole && role != Qt::EditRole) @@ -42,6 +51,7 @@ QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const property); } +// Set data for the index and role specified bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant &value, int role) { if (role != Qt::EditRole || dataModel_.isPropertyFlagSet(index.column(), PropertyFlag::ReadOnly)) @@ -52,14 +62,14 @@ bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant & switch (dataModel_.propertyType(index.column())) { case (PropertyType::Integer): - success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::DataItemValue(value.toInt())); + success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::PropertyValue(value.toInt())); break; case (PropertyType::Double): - success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::DataItemValue(value.toDouble())); + success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::PropertyValue(value.toDouble())); break; case (PropertyType::String): success = dataModel_.setProperty(index.row(), index.column(), - DataModelBase::DataItemValue(value.toString().toStdString())); + DataModelBase::PropertyValue(value.toString().toStdString())); break; default: Messenger::error("DataTableModelInterface doesn't know how to handle this PropertyType.\n"); @@ -72,16 +82,17 @@ bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant & return success; } +// Insert one or more rows at the specified position bool DataTableModelInterface::insertRows(int row, int count, const QModelIndex &parent) { - // TODO - Q_UNUSED(count); - beginInsertRows(parent, row, row); - // parentNode_->addParameter("NewParameter", 0.0, row); - endInsertRows(); + Q_UNUSED(parent); + + dataModel_.createItems(row, count); + return true; } +// Remove one or more rows starting from the specified position bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex &parent) { // TODO diff --git a/src/gui/dataTableModelInterface.h b/src/gui/dataTableModelInterface.h index 4da2f172c3..4702faf322 100644 --- a/src/gui/dataTableModelInterface.h +++ b/src/gui/dataTableModelInterface.h @@ -9,7 +9,7 @@ #include #include -// Qt Interface to DataTableModel +// QAbstractTableModel Interface to DataTableModel class DataTableModelInterface : public QAbstractTableModel { Q_OBJECT @@ -21,12 +21,24 @@ class DataTableModelInterface : public QAbstractTableModel // Model with which to interface DataModelBase &dataModel_; + /* + * QAbstractTableModel Overrides + */ + private: + // Return row count int rowCount(const QModelIndex &parent) const override; + // Return column count int columnCount(const QModelIndex &parent) const override; + // Return flags for the specified model index Qt::ItemFlags flags(const QModelIndex &index) const override; + // Return header data for the specified section, orientation, and role QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + // Return data for the index and role specified QVariant data(const QModelIndex &index, int role) const override; + // Set data for the index and role specified bool setData(const QModelIndex &index, const QVariant &value, int role) override; + // Insert one or more rows at the specified position bool insertRows(int row, int count, const QModelIndex &parent) override; + // Remove one or more rows starting from the specified position bool removeRows(int row, int count, const QModelIndex &parent) override; }; From e590227458a4e2781791af2c1f6d6ff97dec206c Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Wed, 10 Jul 2024 21:57:34 +0100 Subject: [PATCH 09/42] Rename. --- .../keywordWidgets/expressionVariableVector.h | 2 +- src/keywords/expressionVariableVector.cpp | 10 +++---- src/templates/dataModel.h | 30 ++++++++++++------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/gui/keywordWidgets/expressionVariableVector.h b/src/gui/keywordWidgets/expressionVariableVector.h index 3e9bf83b49..32b38e8362 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.h +++ b/src/gui/keywordWidgets/expressionVariableVector.h @@ -3,9 +3,9 @@ #pragma once +#include "gui/dataTableModelInterface.h" #include "gui/keywordWidgets/base.h" #include "gui/keywordWidgets/ui_expressionVariableVector.h" -#include "gui/dataTableModelInterface.h" #include "keywords/expressionVariableVector.h" // Forward Declarations diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index b8583b0c35..330f8ed952 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -29,17 +29,17 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vectorbaseName()); + return DataModelBase::PropertyValue(var->baseName()); case (ExpressionVariableProperties::Type): - return DataModelBase::DataItemValue( + return DataModelBase::PropertyValue( std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); case (ExpressionVariableProperties::Value): - return DataModelBase::DataItemValue(var->value().asString()); + return DataModelBase::PropertyValue(var->value().asString()); default: - return DataModelBase::DataItemValue(); + return DataModelBase::PropertyValue(); } }, - [&](std::shared_ptr &var, int propertyIndex, const DataModelBase::DataItemValue &newValue) + [&](std::shared_ptr &var, int propertyIndex, const DataModelBase::PropertyValue &newValue) { switch (propertyIndex) { diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h index 8a46ae4481..eddf0bfc51 100644 --- a/src/templates/dataModel.h +++ b/src/templates/dataModel.h @@ -24,12 +24,12 @@ enum PropertyFlag // Index / Column ID, Name / Column Title, Data Type, ReadOnly? using DataItemProperty = std::tuple>; -// Helper type for DataItemValue visitor +// Helper type for PropertyValue visitor template struct DataItemVisitor : Ts... { using Ts::operator()...; }; -// Explicit deduction guide for DataItemValue visitor +// Explicit deduction guide for PropertyValue visitor template DataItemVisitor(Ts...) -> DataItemVisitor; /* @@ -42,12 +42,13 @@ class DataModelBase DataModelBase(const std::vector &itemProperties) : itemProperties_(itemProperties) {} /* - * Value Handling + * Property Values */ public: - using DataItemValue = std::variant; + // Property value variant + using PropertyValue = std::variant; // Return value as string - static std::string asString(const DataItemValue &value) + static std::string asString(const PropertyValue &value) { if (std::holds_alternative(value)) return fmt::format("{}", std::get(value)); @@ -83,17 +84,24 @@ class DataModelBase // Return number of properties (i.e. columns) for the specified index virtual int nProperties() = 0; // Set property function - virtual bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) = 0; + virtual bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) = 0; // Get property function - virtual DataItemValue getProperty(int dataIndex, int propertyIndex) = 0; + virtual PropertyValue getProperty(int dataIndex, int propertyIndex) = 0; + + /* + * Item Management + */ + public: + // Create new item(s) starting at specified vector index + virtual void createItems(int index, int count) = 0; }; template class DataTableModel : public DataModelBase { public: // Data access functions - using PropertySetFunction = std::function; - using PropertyGetFunction = std::function; + using PropertySetFunction = std::function; + using PropertyGetFunction = std::function; DataTableModel(std::vector &data, const std::vector &itemProperties) : DataModelBase(itemProperties), data_(data) { @@ -135,7 +143,7 @@ template class DataTableModel : public DataModelBase setPropertyFunction_ = std::move(setFunction); } // Set property - bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) final + bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) final { // Check index validity if (!isIndexValid(dataIndex, propertyIndex)) @@ -154,7 +162,7 @@ template class DataTableModel : public DataModelBase return setPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex]), newValue); } // Get property - DataItemValue getProperty(int dataIndex, int propertyIndex) final + PropertyValue getProperty(int dataIndex, int propertyIndex) final { // Check index validity if (!isIndexValid(dataIndex, propertyIndex)) From 62311f6202ac6e5a887766e5ac71495bcec87c8b Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Wed, 10 Jul 2024 21:59:51 +0100 Subject: [PATCH 10/42] Remove testing code from ExpressionVariableVectorKeyword. --- src/keywords/expressionVariableVector.h | 159 +----------------------- 1 file changed, 1 insertion(+), 158 deletions(-) diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index c5ab6a7c6d..7790a1f206 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -5,169 +5,12 @@ #include "expression/node.h" #include "keywords/base.h" -#include +#include "templates/dataModel.h" // Forward Declarations class ExpressionVariable; class ProcedureNode; -// Data property types -enum class PropertyType -{ - Integer, - Double, - String -}; - -// Data property flags -enum PropertyFlag -{ - ReadOnly -}; - -// Index / Column ID, Name / Column Title, Data Type, ReadOnly? -using DataItemProperty = std::tuple>; - -// Helper type for DataItemValue visitor -template struct DataItemVisitor : Ts... -{ - using Ts::operator()...; -}; -// Explicit deduction guide for DataItemValue visitor -template DataItemVisitor(Ts...) -> DataItemVisitor; - -class DataModelBase -{ - public: - DataModelBase(const std::vector &itemProperties) : itemProperties_(itemProperties) {} - - /* - * Value Handling - */ - public: - using DataItemValue = std::variant; - // Return value as string - static std::string asString(const DataItemValue &value) - { - if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else - return std::get(value); - } - - /* - * Properties - */ - protected: - // Descriptions of relevant item properties within a single object in the container - std::vector itemProperties_; - - public: - // Return name of specified property - std::string propertyName(int propertyIndex) { return std::get<1>(itemProperties_[propertyIndex]); } - // Return property type for the specified column - PropertyType propertyType(int propertyIndex) { return std::get<2>(itemProperties_[propertyIndex]); } - // Return whether the specified property flag is set - bool isPropertyFlagSet(int propertyIndex, PropertyFlag flag) - { - return std::get<3>(itemProperties_[propertyIndex]).isSet(flag); - } - - public: - // Return number of data items (i.e. rows) for the specified index - virtual int nDataItems() = 0; - // Return number of properties (i.e. columns) for the specified index - virtual int nProperties() = 0; - // Set property function - virtual bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) = 0; - // Get property function - virtual DataItemValue getProperty(int dataIndex, int propertyIndex) = 0; -}; - -template class DataTableModel : public DataModelBase -{ - public: - // Data access functions - using PropertySetFunction = std::function; - using PropertyGetFunction = std::function; - DataTableModel(std::vector &data, const std::vector &itemProperties) - : DataModelBase(itemProperties), data_(data) - { - } - - private: - // Target data for the model - std::vector &data_; - - /* - * Extent - */ - private: - // Return whether the supplied index is valid - bool isIndexValid(int dataIndex, int propertyIndex) const - { - return dataIndex >= 0 && dataIndex < data_.size() && propertyIndex >= 0 && propertyIndex < itemProperties_.size(); - } - - public: - // Return number of data items (rows) in the table - int nDataItems() final { return data_.size(); } - // Return number of properties per data item (i.e. columns) in the table - int nProperties() final { return itemProperties_.size(); } - - /* - * Data Access - */ - private: - // Set / get functions, unique per class - PropertySetFunction setPropertyFunction_; - PropertyGetFunction getPropertyFunction_; - - public: - // Set property access functions - void setPropertyFunctions(PropertyGetFunction getFunction, PropertySetFunction setFunction) - { - getPropertyFunction_ = std::move(getFunction); - setPropertyFunction_ = std::move(setFunction); - } - // Set property - bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) final - { - // Check index validity - if (!isIndexValid(dataIndex, propertyIndex)) - return false; - - if (std::get<3>(itemProperties_[propertyIndex]).isSet(PropertyFlag::ReadOnly)) - { - fmt::print("Refusing to set data '{}' since it is read-only.\n", std::get<1>(itemProperties_[propertyIndex])); - return false; - } - - // Set the child at the specified index - if (!setPropertyFunction_) - return false; - else - return setPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex]), newValue); - } - // Get property - DataItemValue getProperty(int dataIndex, int propertyIndex) final - { - // Check index validity - if (!isIndexValid(dataIndex, propertyIndex)) - return {}; - - // Set the child at the specified index - if (!getPropertyFunction_) - return {}; - else - return getPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex])); - } -}; - // Keyword managing vector of ExpressionVariable class ExpressionVariableVectorKeyword : public KeywordBase { From 32d9d807f2b5d1ee13de3ccb674d3044d10f84cf Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Wed, 10 Jul 2024 23:18:31 +0100 Subject: [PATCH 11/42] Begin implementing data mutation links. --- src/gui/dataTableModelInterface.cpp | 31 ++++++++++++++++++- src/gui/dataTableModelInterface.h | 9 +++++- .../expressionVariableVector.cpp | 4 ++- src/keywords/expressionVariableVector.cpp | 4 --- src/templates/dataModel.h | 31 +++++++++++++++++++ 5 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/gui/dataTableModelInterface.cpp b/src/gui/dataTableModelInterface.cpp index 042c26f42f..203363f257 100644 --- a/src/gui/dataTableModelInterface.cpp +++ b/src/gui/dataTableModelInterface.cpp @@ -4,7 +4,11 @@ #include "gui/dataTableModelInterface.h" #include "procedure/nodes/node.h" -DataTableModelInterface::DataTableModelInterface(DataModelBase &dataModel) : dataModel_(dataModel) {} +DataTableModelInterface::DataTableModelInterface(DataModelBase &dataModel) : dataModel_(dataModel) +{ + dataModel_.setMutationSignalFunction([this](DataModelBase::MutationSignal signal, int startIndex, int endIndex) + { dataMutated(signal, startIndex, endIndex); }); +} /* * QAbstractTableModel Overrides @@ -107,3 +111,28 @@ bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex & endRemoveRows(); return true; } + +/* + * Mutation Interface + */ + +// React to a mutation in the model +void DataTableModelInterface::dataMutated(DataModelBase::MutationSignal signal, int startIndex, int endIndex) +{ + printf("lkasjdflkasjk\n"); + switch (signal) + { + case (DataModelBase::MutationSignal::DataCreationStarted): + beginInsertRows({}, startIndex, endIndex - startIndex + 1); + break; + case (DataModelBase::MutationSignal::DataCreationFinished): + endInsertRows(); + break; + case (DataModelBase::MutationSignal::DataRemovalStarted): + beginRemoveRows({}, startIndex, endIndex - startIndex + 1); + break; + case (DataModelBase::MutationSignal::DataRemovalFinished): + endRemoveRows(); + break; + } +} diff --git a/src/gui/dataTableModelInterface.h b/src/gui/dataTableModelInterface.h index 4702faf322..947cbe14ef 100644 --- a/src/gui/dataTableModelInterface.h +++ b/src/gui/dataTableModelInterface.h @@ -24,7 +24,7 @@ class DataTableModelInterface : public QAbstractTableModel /* * QAbstractTableModel Overrides */ - private: + public: // Return row count int rowCount(const QModelIndex &parent) const override; // Return column count @@ -41,4 +41,11 @@ class DataTableModelInterface : public QAbstractTableModel bool insertRows(int row, int count, const QModelIndex &parent) override; // Remove one or more rows starting from the specified position bool removeRows(int row, int count, const QModelIndex &parent) override; + + /* + * Mutation Interface + */ + private: + // React to a mutation in the model + void dataMutated(DataModelBase::MutationSignal signal, int startIndex, int endIndex); }; diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index 4c545e4826..f903938988 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -13,6 +13,7 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWi { // Create and set up the UI for our widget ui_.setupUi(this); + printf("hhuhhuhhuh\n"); // Set model ui_.VariablesTable->setModel(&variableModel_); @@ -46,7 +47,8 @@ void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItem ui_.RemoveVariableButton->setEnabled(current.empty()); } -void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) {} +void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) { + printf("bbbbbboooppp\n"); variableModel_.insertRows(0, 1, {}); } void ExpressionVariableVectorKeywordWidget::ui_RemoveVariableButton_clicked(bool checked) {} diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 330f8ed952..2d99646618 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -163,7 +163,3 @@ void ExpressionVariableVectorKeyword::deserialise(const SerialisedValue &node, c toMap(node, [this](const auto &key, const auto &value) { parentNode_->addParameter(key, toml::get(value)); }); } - -/* - * Object Management - */ diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h index eddf0bfc51..10db5ebc91 100644 --- a/src/templates/dataModel.h +++ b/src/templates/dataModel.h @@ -92,6 +92,25 @@ class DataModelBase * Item Management */ public: + enum class MutationSignal + { + PropertyChanged, + DataCreationStarted, + DataCreationFinished, + DataRemovalStarted, + DataRemovalFinished + }; + using DataMutationSignalFunction = std::function; + DataMutationSignalFunction mutationSignalFunction_ = {}; + void setMutationSignalFunction(DataMutationSignalFunction mutationSignalFunction) + { + mutationSignalFunction_ = std::move(mutationSignalFunction); + } + void emitMutationSignal(MutationSignal signal, int startIndex, int endIndex) + { + if (mutationSignalFunction_) + mutationSignalFunction_(signal, startIndex, endIndex); + } // Create new item(s) starting at specified vector index virtual void createItems(int index, int count) = 0; }; @@ -174,4 +193,16 @@ template class DataTableModel : public DataModelBase else return getPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex])); } + + /* + * Item Management + */ + public: + // Create new item(s) starting at specified vector index + void createItems(int index, int count) final + { + emitMutationSignal(DataModelBase::MutationSignal::DataCreationStarted, index, index + count); + data_.insert(data_.begin() + index, DataItem()); + emitMutationSignal(DataModelBase::MutationSignal::DataCreationFinished, index, index + count); + } }; From f8541f6ff8693bdaa70f6dcc0ecc61edb5d8532e Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 16 Jul 2024 11:19:59 +0100 Subject: [PATCH 12/42] Tidy up. --- src/gui/dataTableModelInterface.h | 6 +----- src/templates/dataModel.h | 3 +++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/gui/dataTableModelInterface.h b/src/gui/dataTableModelInterface.h index 947cbe14ef..a737580e75 100644 --- a/src/gui/dataTableModelInterface.h +++ b/src/gui/dataTableModelInterface.h @@ -3,17 +3,13 @@ #pragma once -#include "keywords/expressionVariableVector.h" -#include "templates/optionalRef.h" +#include "templates/dataModel.h" #include #include -#include // QAbstractTableModel Interface to DataTableModel class DataTableModelInterface : public QAbstractTableModel { - Q_OBJECT - public: DataTableModelInterface(DataModelBase &dataModel); diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h index 10db5ebc91..7ff6165446 100644 --- a/src/templates/dataModel.h +++ b/src/templates/dataModel.h @@ -4,8 +4,11 @@ #pragma once #include "templates/flags.h" +#include +#include #include #include +#include // Data property types enum class PropertyType From 5701c529907fa217048947b1a2241bdeed2e47fc Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 16 Jul 2024 14:00:21 +0100 Subject: [PATCH 13/42] Insert and append items. --- src/gui/dataTableModelInterface.cpp | 15 ++++++-- src/gui/dataTableModelInterface.h | 2 + src/keywords/expressionVariableVector.cpp | 8 ++++ src/templates/dataModel.h | 47 +++++++++++++++++++---- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/gui/dataTableModelInterface.cpp b/src/gui/dataTableModelInterface.cpp index 203363f257..c09fe1d817 100644 --- a/src/gui/dataTableModelInterface.cpp +++ b/src/gui/dataTableModelInterface.cpp @@ -96,6 +96,16 @@ bool DataTableModelInterface::insertRows(int row, int count, const QModelIndex & return true; } +// Append one or more rows +bool DataTableModelInterface::appendRows(int count, const QModelIndex &parent) +{ + Q_UNUSED(parent); + + dataModel_.appendItems(count); + + return true; +} + // Remove one or more rows starting from the specified position bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex &parent) { @@ -119,17 +129,16 @@ bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex & // React to a mutation in the model void DataTableModelInterface::dataMutated(DataModelBase::MutationSignal signal, int startIndex, int endIndex) { - printf("lkasjdflkasjk\n"); switch (signal) { case (DataModelBase::MutationSignal::DataCreationStarted): - beginInsertRows({}, startIndex, endIndex - startIndex + 1); + beginInsertRows({}, startIndex, endIndex); break; case (DataModelBase::MutationSignal::DataCreationFinished): endInsertRows(); break; case (DataModelBase::MutationSignal::DataRemovalStarted): - beginRemoveRows({}, startIndex, endIndex - startIndex + 1); + beginRemoveRows({}, startIndex, endIndex); break; case (DataModelBase::MutationSignal::DataRemovalFinished): endRemoveRows(); diff --git a/src/gui/dataTableModelInterface.h b/src/gui/dataTableModelInterface.h index a737580e75..4bb10f6885 100644 --- a/src/gui/dataTableModelInterface.h +++ b/src/gui/dataTableModelInterface.h @@ -35,6 +35,8 @@ class DataTableModelInterface : public QAbstractTableModel bool setData(const QModelIndex &index, const QVariant &value, int role) override; // Insert one or more rows at the specified position bool insertRows(int row, int count, const QModelIndex &parent) override; + // Append one or more rows + bool appendRows(int count, const QModelIndex &parent); // Remove one or more rows starting from the specified position bool removeRows(int row, int count, const QModelIndex &parent) override; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 2d99646618..17e024d49f 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -75,6 +75,14 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector insertAt) + { + auto allParamters = parentNode_->getParametersInScope(); + return parentNode_->addParameter( + DissolveSys::uniqueName("NewVariable", allParamters, [](const auto &var) { return var->name(); }), {}, + insertAt); + }); } /* diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h index 7ff6165446..0da9ca9a66 100644 --- a/src/templates/dataModel.h +++ b/src/templates/dataModel.h @@ -6,6 +6,7 @@ #include "templates/flags.h" #include #include +#include #include #include #include @@ -109,21 +110,20 @@ class DataModelBase { mutationSignalFunction_ = std::move(mutationSignalFunction); } - void emitMutationSignal(MutationSignal signal, int startIndex, int endIndex) + void emitMutationSignal(MutationSignal signal, int startIndex = 0, int endIndex = 0) { if (mutationSignalFunction_) mutationSignalFunction_(signal, startIndex, endIndex); } // Create new item(s) starting at specified vector index virtual void createItems(int index, int count) = 0; + // Append new item(s) to the end of the data + virtual void appendItems(int count) = 0; }; template class DataTableModel : public DataModelBase { public: - // Data access functions - using PropertySetFunction = std::function; - using PropertyGetFunction = std::function; DataTableModel(std::vector &data, const std::vector &itemProperties) : DataModelBase(itemProperties), data_(data) { @@ -153,8 +153,11 @@ template class DataTableModel : public DataModelBase * Data Access */ private: - // Set / get functions, unique per class + // Data setter function - unique to DataItem class + using PropertySetFunction = std::function; PropertySetFunction setPropertyFunction_; + // Data getter function - unique to DataItem class + using PropertyGetFunction = std::function; PropertyGetFunction getPropertyFunction_; public: @@ -200,12 +203,40 @@ template class DataTableModel : public DataModelBase /* * Item Management */ + private: + // Creation function (if required) otherwise default constructor T() will be called + using CreateItemFunction = std::function)>; + CreateItemFunction createItemFunction_; + + public: + // Set data creation function + void setDataCreationFunction(CreateItemFunction function) { createItemFunction_ = std::move(function); } + public: // Create new item(s) starting at specified vector index void createItems(int index, int count) final { - emitMutationSignal(DataModelBase::MutationSignal::DataCreationStarted, index, index + count); - data_.insert(data_.begin() + index, DataItem()); - emitMutationSignal(DataModelBase::MutationSignal::DataCreationFinished, index, index + count); + emitMutationSignal(DataModelBase::MutationSignal::DataCreationStarted, index, index + count - 1); + for (auto n = 0; n < count; ++n) + { + if (createItemFunction_) + createItemFunction_(index + n); + else + data_.insert(data_.begin() + index, DataItem()); + } + emitMutationSignal(DataModelBase::MutationSignal::DataCreationFinished); + } + // Append new item(s) to the end of the data + void appendItems(int count) final + { + emitMutationSignal(DataModelBase::MutationSignal::DataCreationStarted, data_.size(), data_.size() + count - 1); + for (auto n = 0; n < count; ++n) + { + if (createItemFunction_) + createItemFunction_({}); + else + data_.emplace_back(DataItem()); + } + emitMutationSignal(DataModelBase::MutationSignal::DataCreationFinished); } }; From 0e785f051ef560500a0faa97bdd5563d19d6f0a9 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 16 Jul 2024 14:01:44 +0100 Subject: [PATCH 14/42] Fix, tidy. --- src/gui/keywordWidgets/expressionVariableVector.cpp | 6 ++---- src/gui/keywordWidgets/expressionVariableVector.h | 7 ++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index f903938988..00b04d996d 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -13,7 +13,6 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWi { // Create and set up the UI for our widget ui_.setupUi(this); - printf("hhuhhuhhuh\n"); // Set model ui_.VariablesTable->setModel(&variableModel_); @@ -47,10 +46,9 @@ void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItem ui_.RemoveVariableButton->setEnabled(current.empty()); } -void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) { - printf("bbbbbboooppp\n"); variableModel_.insertRows(0, 1, {}); } +void ExpressionVariableVectorKeywordWidget::on_AddVariableButton_clicked(bool checked) { variableModel_.appendRows(1, {}); } -void ExpressionVariableVectorKeywordWidget::ui_RemoveVariableButton_clicked(bool checked) {} +void ExpressionVariableVectorKeywordWidget::on_RemoveVariableButton_clicked(bool checked) {} // Update value displayed in widget void ExpressionVariableVectorKeywordWidget::updateValue(const Flags &mutationFlags) {} diff --git a/src/gui/keywordWidgets/expressionVariableVector.h b/src/gui/keywordWidgets/expressionVariableVector.h index 32b38e8362..82d2992955 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.h +++ b/src/gui/keywordWidgets/expressionVariableVector.h @@ -8,9 +8,6 @@ #include "gui/keywordWidgets/ui_expressionVariableVector.h" #include "keywords/expressionVariableVector.h" -// Forward Declarations -class QWidget; - class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidgetBase { // All Qt declarations must include this macro @@ -38,8 +35,8 @@ class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidg private Q_SLOTS: void variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void variableSelectionChanged(const QItemSelection ¤t, const QItemSelection &previous); - void ui_AddVariableButton_clicked(bool checked); - void ui_RemoveVariableButton_clicked(bool checked); + void on_AddVariableButton_clicked(bool checked); + void on_RemoveVariableButton_clicked(bool checked); Q_SIGNALS: // Keyword data changed From bfb5b7eb38f63b106036b0dd3606d90ccf693d30 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 16 Jul 2024 14:05:21 +0100 Subject: [PATCH 15/42] Fix logic, update selection behaviour. --- src/gui/keywordWidgets/expressionVariableVector.cpp | 2 +- src/gui/keywordWidgets/expressionVariableVector.ui | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index 00b04d996d..e7cc80c574 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -43,7 +43,7 @@ void ExpressionVariableVectorKeywordWidget::variableDataChanged(const QModelInde void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItemSelection ¤t, const QItemSelection &previous) { - ui_.RemoveVariableButton->setEnabled(current.empty()); + ui_.RemoveVariableButton->setEnabled(!current.empty()); } void ExpressionVariableVectorKeywordWidget::on_AddVariableButton_clicked(bool checked) { variableModel_.appendRows(1, {}); } diff --git a/src/gui/keywordWidgets/expressionVariableVector.ui b/src/gui/keywordWidgets/expressionVariableVector.ui index 97ff965b02..ae928423a6 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.ui +++ b/src/gui/keywordWidgets/expressionVariableVector.ui @@ -7,7 +7,7 @@ 0 0 194 - 99 + 106 @@ -51,6 +51,9 @@ QAbstractItemView::SingleSelection + + QAbstractItemView::SelectRows + From ded7b15d1b87e7bbf231546b904f845d88722fdb Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 16 Jul 2024 14:53:20 +0100 Subject: [PATCH 16/42] Remove rows. --- src/gui/dataTableModelInterface.cpp | 12 +++------- .../expressionVariableVector.cpp | 5 ++++- src/templates/dataModel.h | 22 ++++++++++++++++++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/gui/dataTableModelInterface.cpp b/src/gui/dataTableModelInterface.cpp index c09fe1d817..ee155ac9b1 100644 --- a/src/gui/dataTableModelInterface.cpp +++ b/src/gui/dataTableModelInterface.cpp @@ -109,16 +109,10 @@ bool DataTableModelInterface::appendRows(int count, const QModelIndex &parent) // Remove one or more rows starting from the specified position bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex &parent) { - // TODO - Q_UNUSED(count); - if (row >= rowCount(parent) || row < 0) - { - return false; - } + Q_UNUSED(parent); + + dataModel_.removeItems(row, count); - beginRemoveRows(parent, row, row); - // ranges_->get().erase(ranges_->get().begin() + row); - endRemoveRows(); return true; } diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index e7cc80c574..8fa2f7327a 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -48,7 +48,10 @@ void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItem void ExpressionVariableVectorKeywordWidget::on_AddVariableButton_clicked(bool checked) { variableModel_.appendRows(1, {}); } -void ExpressionVariableVectorKeywordWidget::on_RemoveVariableButton_clicked(bool checked) {} +void ExpressionVariableVectorKeywordWidget::on_RemoveVariableButton_clicked(bool checked) +{ + variableModel_.removeRows(ui_.VariablesTable->selectionModel()->currentIndex().row(), 1, {}); +} // Update value displayed in widget void ExpressionVariableVectorKeywordWidget::updateValue(const Flags &mutationFlags) {} diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h index 0da9ca9a66..ecbc5d5f99 100644 --- a/src/templates/dataModel.h +++ b/src/templates/dataModel.h @@ -119,6 +119,8 @@ class DataModelBase virtual void createItems(int index, int count) = 0; // Append new item(s) to the end of the data virtual void appendItems(int count) = 0; + // Remove item(s) starting at specified vector index + virtual void removeItems(int index, int count) = 0; }; template class DataTableModel : public DataModelBase @@ -204,13 +206,18 @@ template class DataTableModel : public DataModelBase * Item Management */ private: - // Creation function (if required) otherwise default constructor T() will be called + // Item creation function (if required) otherwise default constructor T() will be called using CreateItemFunction = std::function)>; CreateItemFunction createItemFunction_; + // Item removal function (if required) + using RemoveItemFunction = std::function; + RemoveItemFunction removeItemFunction_; public: // Set data creation function void setDataCreationFunction(CreateItemFunction function) { createItemFunction_ = std::move(function); } + // Set data removal function + void setDataRemovalFunction(RemoveItemFunction function) { removeItemFunction_ = std::move(function); } public: // Create new item(s) starting at specified vector index @@ -239,4 +246,17 @@ template class DataTableModel : public DataModelBase } emitMutationSignal(DataModelBase::MutationSignal::DataCreationFinished); } + // Remove item(s) starting at specified vector index + void removeItems(int index, int count) final + { + emitMutationSignal(DataModelBase::MutationSignal::DataRemovalStarted, index, index + count - 1); + if (removeItemFunction_) + { + for (auto n = 0; n < count; ++n) + removeItemFunction_(index); + } + else + data_.erase(data_.begin() + index, data_.begin() + index + count); + emitMutationSignal(DataModelBase::MutationSignal::DataRemovalFinished); + } }; From 026cd708b6ec0a6bb0e00c87b325cf4a82ec9dd1 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 17 Jul 2024 13:06:48 +0100 Subject: [PATCH 17/42] User per-property setters and getters. --- src/gui/dataTableModelInterface.cpp | 8 +- src/keywords/expressionVariableVector.cpp | 95 ++++++---------- src/templates/dataModel.h | 127 ++++++++++++++-------- 3 files changed, 118 insertions(+), 112 deletions(-) diff --git a/src/gui/dataTableModelInterface.cpp b/src/gui/dataTableModelInterface.cpp index ee155ac9b1..5907dfb695 100644 --- a/src/gui/dataTableModelInterface.cpp +++ b/src/gui/dataTableModelInterface.cpp @@ -58,20 +58,20 @@ QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const // Set data for the index and role specified bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant &value, int role) { - if (role != Qt::EditRole || dataModel_.isPropertyFlagSet(index.column(), PropertyFlag::ReadOnly)) + if (role != Qt::EditRole || dataModel_.isPropertyFlagSet(index.column(), DataItemProperty::PropertyFlag::ReadOnly)) return false; // Set new value bool success = false; switch (dataModel_.propertyType(index.column())) { - case (PropertyType::Integer): + case (DataItemProperty::PropertyType::Integer): success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::PropertyValue(value.toInt())); break; - case (PropertyType::Double): + case (DataItemProperty::PropertyType::Double): success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::PropertyValue(value.toDouble())); break; - case (PropertyType::String): + case (DataItemProperty::PropertyType::String): success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::PropertyValue(value.toString().toStdString())); break; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 17e024d49f..eed508a494 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -8,79 +8,50 @@ #include "procedure/nodes/node.h" #include -enum ExpressionVariableProperties -{ - Name, - Type, - Value -}; -std::vector expressionVariableProperties = { - {ExpressionVariableProperties::Name, "Name", PropertyType::String, {}}, - {ExpressionVariableProperties::Type, "Type", PropertyType::String, {PropertyFlag::ReadOnly}}, - {ExpressionVariableProperties::Value, "Value", PropertyType::String, {}}}; - ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &data, ProcedureNode *parentNode) - : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_, expressionVariableProperties) + : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_) { - dataModel_.setPropertyFunctions( - [&](const std::shared_ptr &var, int propertyIndex) + dataModel_.addProperty( + "Name", DataItemProperty::PropertyType::String, {}, + [&](const std::shared_ptr &var) { return DataModelBase::PropertyValue(var->baseName()); }, + [&](std::shared_ptr &var, const DataModelBase::PropertyValue &newValue) { - switch (propertyIndex) - { - case (ExpressionVariableProperties::Name): - return DataModelBase::PropertyValue(var->baseName()); - case (ExpressionVariableProperties::Type): - return DataModelBase::PropertyValue( - std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); - case (ExpressionVariableProperties::Value): - return DataModelBase::PropertyValue(var->value().asString()); - default: - return DataModelBase::PropertyValue(); - } - }, - [&](std::shared_ptr &var, int propertyIndex, const DataModelBase::PropertyValue &newValue) + // Must check for existing var in scope with the same name + auto p = parentNode_->getParameter(DataModelBase::asString(newValue)); + if (p && p != var) + return false; + var->setBaseName(DataModelBase::asString(newValue)); + return true; + }); + dataModel_.addProperty("Type", DataItemProperty::PropertyType::String, {}, + [&](const std::shared_ptr &var) + { + return DataModelBase::PropertyValue( + std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); + }); + dataModel_.addProperty( + "Value", DataItemProperty::PropertyType::String, {}, + [&](const std::shared_ptr &var) { return DataModelBase::PropertyValue(var->value().asString()); }, + [&](std::shared_ptr &var, const DataModelBase::PropertyValue &newValue) { - switch (propertyIndex) - { - case (ExpressionVariableProperties::Name): - { - // Must check for existing var in scope with the same name - auto newName = DataModelBase::asString(newValue); - auto p = parentNode_->getParameter(newName); - if (p && p != var) - return false; - var->setBaseName(newName); - } - break; - case (ExpressionVariableProperties::Value): - { - // Value - need to check type (int vs double) - bool isFloatingPoint = false; - auto value = DataModelBase::asString(newValue); - if (DissolveSys::isNumber(value, isFloatingPoint)) - { - if (isFloatingPoint) - var->setValue(std::stod(value)); - else - var->setValue(std::stoi(value)); - } - else - return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", value, - var->baseName()); - } - break; - default: - return false; - } + // Need to check type (int vs double) + auto isFloatingPoint = false; + auto value = DataModelBase::asString(newValue); + if (!DissolveSys::isNumber(value, isFloatingPoint)) + return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", value, + var->baseName()); + + isFloatingPoint ? var->setValue(std::stod(value)) : var->setValue(std::stoi(value)); return true; }); + dataModel_.setDataCreationFunction( [&](std::optional insertAt) { - auto allParamters = parentNode_->getParametersInScope(); + auto allParameters = parentNode_->getParametersInScope(); return parentNode_->addParameter( - DissolveSys::uniqueName("NewVariable", allParamters, [](const auto &var) { return var->name(); }), {}, + DissolveSys::uniqueName("NewVariable", allParameters, [](const auto &var) { return var->name(); }), {}, insertAt); }); } diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h index ecbc5d5f99..180a3c0db2 100644 --- a/src/templates/dataModel.h +++ b/src/templates/dataModel.h @@ -6,27 +6,50 @@ #include "templates/flags.h" #include #include +#include #include #include #include #include -// Data property types -enum class PropertyType +// Index / Column ID, Name / Column Title, Data Type, ReadOnly? +class DataItemProperty { - Integer, - Double, - String -}; + public: + // Data property types + enum class PropertyType + { + Integer, + Double, + String + }; -// Data property flags -enum PropertyFlag -{ - ReadOnly -}; + // Data property flags + enum PropertyFlag + { + ReadOnly + }; + DataItemProperty(std::string_view name, PropertyType type, Flags flags = {}) + : name_{name}, type_(type), flags_(flags) + { + } -// Index / Column ID, Name / Column Title, Data Type, ReadOnly? -using DataItemProperty = std::tuple>; + private: + // Property name + std::string name_; + // Property type + PropertyType type_; + // Property flags + Flags flags_; + + public: + // Return property name + const std::string &name() const { return name_; } + // Return property type + PropertyType type() const { return type_; } + // Return property flags + const Flags &flags() const { return flags_; } +}; // Helper type for PropertyValue visitor template struct DataItemVisitor : Ts... @@ -38,12 +61,12 @@ template DataItemVisitor(Ts...) -> DataItemVisitor; /* * DataModelBase - An abstract class intended to allow our custom C++ data classes to be accessed in a consistent - * and simplified way through Qt's Model-View classes. + * and simplified way through Qt's Model-View classes, without the need for a specific Qt model for each. */ class DataModelBase { public: - DataModelBase(const std::vector &itemProperties) : itemProperties_(itemProperties) {} + DataModelBase() {} /* * Property Values @@ -72,14 +95,21 @@ class DataModelBase std::vector itemProperties_; public: + // Add item property + void addItemProperty(std::string_view name, DataItemProperty::PropertyType type, + Flags flags = {}) + { + itemProperties_.emplace_back(name, type, flags); + } + // Return name of specified property - std::string propertyName(int propertyIndex) { return std::get<1>(itemProperties_[propertyIndex]); } + std::string propertyName(int propertyIndex) { return itemProperties_[propertyIndex].name(); } // Return property type for the specified column - PropertyType propertyType(int propertyIndex) { return std::get<2>(itemProperties_[propertyIndex]); } + DataItemProperty::PropertyType propertyType(int propertyIndex) { return itemProperties_[propertyIndex].type(); } // Return whether the specified property flag is set - bool isPropertyFlagSet(int propertyIndex, PropertyFlag flag) + bool isPropertyFlagSet(int propertyIndex, DataItemProperty::PropertyFlag flag) { - return std::get<3>(itemProperties_[propertyIndex]).isSet(flag); + return itemProperties_[propertyIndex].flags().isSet(flag); } public: @@ -126,14 +156,37 @@ class DataModelBase template class DataTableModel : public DataModelBase { public: - DataTableModel(std::vector &data, const std::vector &itemProperties) - : DataModelBase(itemProperties), data_(data) - { - } + // Property get function + using PropertyGetFunction = std::function; + // Property set function + using PropertySetFunction = std::function; + DataTableModel(std::vector &data) : DataModelBase(), data_(data) {} + + /* + * Target Data and Functions + */ private: // Target data for the model std::vector &data_; + // Map of named properties to data getters + std::map getters_; + // Map of named properties to data setters + std::map setters_; + + public: + // Add item property for use in the model + void addProperty(const std::string &name, DataItemProperty::PropertyType type, Flags flags, + PropertyGetFunction getter, PropertySetFunction setter = {}) + { + // Add the property base info - the order will be reflected in the table model + addItemProperty(name, type, flags); + + // Store functions + getters_[name] = std::move(getter); + if (setter) + setters_[name] = std::move(setter); + } /* * Extent @@ -154,21 +207,7 @@ template class DataTableModel : public DataModelBase /* * Data Access */ - private: - // Data setter function - unique to DataItem class - using PropertySetFunction = std::function; - PropertySetFunction setPropertyFunction_; - // Data getter function - unique to DataItem class - using PropertyGetFunction = std::function; - PropertyGetFunction getPropertyFunction_; - public: - // Set property access functions - void setPropertyFunctions(PropertyGetFunction getFunction, PropertySetFunction setFunction) - { - getPropertyFunction_ = std::move(getFunction); - setPropertyFunction_ = std::move(setFunction); - } // Set property bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) final { @@ -176,17 +215,17 @@ template class DataTableModel : public DataModelBase if (!isIndexValid(dataIndex, propertyIndex)) return false; - if (std::get<3>(itemProperties_[propertyIndex]).isSet(PropertyFlag::ReadOnly)) + if (itemProperties_[propertyIndex].flags().isSet(DataItemProperty::PropertyFlag::ReadOnly)) { - fmt::print("Refusing to set data '{}' since it is read-only.\n", std::get<1>(itemProperties_[propertyIndex])); + fmt::print("Refusing to set data '{}' since it is read-only.\n", itemProperties_[propertyIndex].name()); return false; } // Set the child at the specified index - if (!setPropertyFunction_) + if (setters_.find(itemProperties_[propertyIndex].name()) == setters_.end()) return false; else - return setPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex]), newValue); + return setters_[itemProperties_[propertyIndex].name()](data_[dataIndex], newValue); } // Get property PropertyValue getProperty(int dataIndex, int propertyIndex) final @@ -195,11 +234,7 @@ template class DataTableModel : public DataModelBase if (!isIndexValid(dataIndex, propertyIndex)) return {}; - // Set the child at the specified index - if (!getPropertyFunction_) - return {}; - else - return getPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex])); + return getters_[itemProperties_[propertyIndex].name()](data_[dataIndex]); } /* From 778b0e503ae74c6a1b267295ab2adb28fc15b0b2 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 17 Jul 2024 13:15:25 +0100 Subject: [PATCH 18/42] Return flags. --- src/gui/dataTableModelInterface.cpp | 12 +++++++----- src/keywords/expressionVariableVector.cpp | 2 +- src/templates/dataModel.h | 10 ++++++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/gui/dataTableModelInterface.cpp b/src/gui/dataTableModelInterface.cpp index 5907dfb695..417aeabe7d 100644 --- a/src/gui/dataTableModelInterface.cpp +++ b/src/gui/dataTableModelInterface.cpp @@ -23,11 +23,13 @@ int DataTableModelInterface::columnCount(const QModelIndex &parent) const { retu // Return flags for the specified model index Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const { - // TODO - if (index.column() == 1) - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; - else - return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; + auto &propertyFlags = dataModel_.propertyFlags(index.column()); + Qt::ItemFlags flags = Qt::ItemIsSelectable; + if (!propertyFlags.isSet(DataItemProperty::ReadOnly)) + flags.setFlag(Qt::ItemIsEditable); + if (!propertyFlags.isSet(DataItemProperty::Disabled)) + flags.setFlag(Qt::ItemIsEnabled); + return flags; } // Return header data for the specified section, orientation, and role diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index eed508a494..918ef65c7c 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -24,7 +24,7 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vectorsetBaseName(DataModelBase::asString(newValue)); return true; }); - dataModel_.addProperty("Type", DataItemProperty::PropertyType::String, {}, + dataModel_.addProperty("Type", DataItemProperty::PropertyType::String, {DataItemProperty::PropertyFlag::ReadOnly}, [&](const std::shared_ptr &var) { return DataModelBase::PropertyValue( diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h index 180a3c0db2..432dc2211e 100644 --- a/src/templates/dataModel.h +++ b/src/templates/dataModel.h @@ -27,7 +27,8 @@ class DataItemProperty // Data property flags enum PropertyFlag { - ReadOnly + ReadOnly, + Disabled }; DataItemProperty(std::string_view name, PropertyType type, Flags flags = {}) : name_{name}, type_(type), flags_(flags) @@ -104,8 +105,13 @@ class DataModelBase // Return name of specified property std::string propertyName(int propertyIndex) { return itemProperties_[propertyIndex].name(); } - // Return property type for the specified column + // Return property type for the specified property DataItemProperty::PropertyType propertyType(int propertyIndex) { return itemProperties_[propertyIndex].type(); } + // Return all flags set for specified property + const Flags &propertyFlags(int propertyIndex) + { + return itemProperties_[propertyIndex].flags(); + } // Return whether the specified property flag is set bool isPropertyFlagSet(int propertyIndex, DataItemProperty::PropertyFlag flag) { From df41e524614bfc2f56f07dcd3b3b8689ac11a76d Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 19 Jul 2024 11:42:09 +0100 Subject: [PATCH 19/42] Namespace and split up. --- src/gui/dataTableModelInterface.cpp | 43 +-- src/gui/dataTableModelInterface.h | 8 +- src/keywords/expressionVariableVector.cpp | 28 +- src/keywords/expressionVariableVector.h | 8 +- src/templates/dataModel.h | 303 ---------------------- src/templates/dataModelBase.h | 91 +++++++ src/templates/dataModelItem.h | 75 ++++++ src/templates/dataModelTable.h | 153 +++++++++++ 8 files changed, 363 insertions(+), 346 deletions(-) delete mode 100644 src/templates/dataModel.h create mode 100644 src/templates/dataModelBase.h create mode 100644 src/templates/dataModelItem.h create mode 100644 src/templates/dataModelTable.h diff --git a/src/gui/dataTableModelInterface.cpp b/src/gui/dataTableModelInterface.cpp index 417aeabe7d..18d0614b6b 100644 --- a/src/gui/dataTableModelInterface.cpp +++ b/src/gui/dataTableModelInterface.cpp @@ -4,9 +4,9 @@ #include "gui/dataTableModelInterface.h" #include "procedure/nodes/node.h" -DataTableModelInterface::DataTableModelInterface(DataModelBase &dataModel) : dataModel_(dataModel) +DataTableModelInterface::DataTableModelInterface(DataModel::Base &dataModel) : dataModel_(dataModel) { - dataModel_.setMutationSignalFunction([this](DataModelBase::MutationSignal signal, int startIndex, int endIndex) + dataModel_.setMutationSignalFunction([this](DataModel::Base::MutationSignal signal, int startIndex, int endIndex) { dataMutated(signal, startIndex, endIndex); }); } @@ -25,9 +25,9 @@ Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const { auto &propertyFlags = dataModel_.propertyFlags(index.column()); Qt::ItemFlags flags = Qt::ItemIsSelectable; - if (!propertyFlags.isSet(DataItemProperty::ReadOnly)) + if (!propertyFlags.isSet(DataModel::ItemProperty::ReadOnly)) flags.setFlag(Qt::ItemIsEditable); - if (!propertyFlags.isSet(DataItemProperty::Disabled)) + if (!propertyFlags.isSet(DataModel::ItemProperty::Disabled)) flags.setFlag(Qt::ItemIsEnabled); return flags; } @@ -51,31 +51,32 @@ QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const auto property = dataModel_.getProperty(index.row(), index.column()); // Construct a QVariant from the contents of our std::variant - return std::visit(DataItemVisitor{[](int arg) { return QVariant(arg); }, [](double arg) { return QVariant(arg); }, - [](std::string_view arg) { return QVariant(QString::fromStdString(std::string(arg))); }, - [](std::string &arg) { return QVariant(QString::fromStdString(arg)); }}, - property); + return std::visit( + DataModel::PropertyVisitor{[](int arg) { return QVariant(arg); }, [](double arg) { return QVariant(arg); }, + [](std::string_view arg) { return QVariant(QString::fromStdString(std::string(arg))); }, + [](std::string &arg) { return QVariant(QString::fromStdString(arg)); }}, + property); } // Set data for the index and role specified bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant &value, int role) { - if (role != Qt::EditRole || dataModel_.isPropertyFlagSet(index.column(), DataItemProperty::PropertyFlag::ReadOnly)) + if (role != Qt::EditRole || dataModel_.isPropertyFlagSet(index.column(), DataModel::ItemProperty::PropertyFlag::ReadOnly)) return false; // Set new value bool success = false; switch (dataModel_.propertyType(index.column())) { - case (DataItemProperty::PropertyType::Integer): - success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::PropertyValue(value.toInt())); + case (DataModel::ItemProperty::PropertyType::Integer): + success = dataModel_.setProperty(index.row(), index.column(), DataModel::PropertyValue(value.toInt())); break; - case (DataItemProperty::PropertyType::Double): - success = dataModel_.setProperty(index.row(), index.column(), DataModelBase::PropertyValue(value.toDouble())); + case (DataModel::ItemProperty::PropertyType::Double): + success = dataModel_.setProperty(index.row(), index.column(), DataModel::PropertyValue(value.toDouble())); break; - case (DataItemProperty::PropertyType::String): - success = dataModel_.setProperty(index.row(), index.column(), - DataModelBase::PropertyValue(value.toString().toStdString())); + case (DataModel::ItemProperty::PropertyType::String): + success = + dataModel_.setProperty(index.row(), index.column(), DataModel::PropertyValue(value.toString().toStdString())); break; default: Messenger::error("DataTableModelInterface doesn't know how to handle this PropertyType.\n"); @@ -123,20 +124,20 @@ bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex & */ // React to a mutation in the model -void DataTableModelInterface::dataMutated(DataModelBase::MutationSignal signal, int startIndex, int endIndex) +void DataTableModelInterface::dataMutated(DataModel::Base::MutationSignal signal, int startIndex, int endIndex) { switch (signal) { - case (DataModelBase::MutationSignal::DataCreationStarted): + case (DataModel::Base::MutationSignal::DataCreationStarted): beginInsertRows({}, startIndex, endIndex); break; - case (DataModelBase::MutationSignal::DataCreationFinished): + case (DataModel::Base::MutationSignal::DataCreationFinished): endInsertRows(); break; - case (DataModelBase::MutationSignal::DataRemovalStarted): + case (DataModel::Base::MutationSignal::DataRemovalStarted): beginRemoveRows({}, startIndex, endIndex); break; - case (DataModelBase::MutationSignal::DataRemovalFinished): + case (DataModel::Base::MutationSignal::DataRemovalFinished): endRemoveRows(); break; } diff --git a/src/gui/dataTableModelInterface.h b/src/gui/dataTableModelInterface.h index 4bb10f6885..0cb141d69c 100644 --- a/src/gui/dataTableModelInterface.h +++ b/src/gui/dataTableModelInterface.h @@ -3,7 +3,7 @@ #pragma once -#include "templates/dataModel.h" +#include "templates/dataModelBase.h" #include #include @@ -11,11 +11,11 @@ class DataTableModelInterface : public QAbstractTableModel { public: - DataTableModelInterface(DataModelBase &dataModel); + DataTableModelInterface(DataModel::Base &dataModel); private: // Model with which to interface - DataModelBase &dataModel_; + DataModel::Base &dataModel_; /* * QAbstractTableModel Overrides @@ -45,5 +45,5 @@ class DataTableModelInterface : public QAbstractTableModel */ private: // React to a mutation in the model - void dataMutated(DataModelBase::MutationSignal signal, int startIndex, int endIndex); + void dataMutated(DataModel::Base::MutationSignal signal, int startIndex, int endIndex); }; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 918ef65c7c..9306f9d0bd 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -13,31 +13,31 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector &var) { return DataModelBase::PropertyValue(var->baseName()); }, - [&](std::shared_ptr &var, const DataModelBase::PropertyValue &newValue) + "Name", DataModel::ItemProperty::PropertyType::String, {}, + [&](const std::shared_ptr &var) { return DataModel::PropertyValue(var->baseName()); }, + [&](std::shared_ptr &var, const DataModel::PropertyValue &newValue) { // Must check for existing var in scope with the same name - auto p = parentNode_->getParameter(DataModelBase::asString(newValue)); + auto p = parentNode_->getParameter(DataModel::propertyAsString(newValue)); if (p && p != var) return false; - var->setBaseName(DataModelBase::asString(newValue)); + var->setBaseName(DataModel::propertyAsString(newValue)); return true; }); - dataModel_.addProperty("Type", DataItemProperty::PropertyType::String, {DataItemProperty::PropertyFlag::ReadOnly}, - [&](const std::shared_ptr &var) - { - return DataModelBase::PropertyValue( + dataModel_.addProperty("Type", DataModel::ItemProperty::PropertyType::String, + {DataModel::ItemProperty::PropertyFlag::ReadOnly}, + [&](const std::shared_ptr &var) { + return DataModel::PropertyValue( std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); }); dataModel_.addProperty( - "Value", DataItemProperty::PropertyType::String, {}, - [&](const std::shared_ptr &var) { return DataModelBase::PropertyValue(var->value().asString()); }, - [&](std::shared_ptr &var, const DataModelBase::PropertyValue &newValue) + "Value", DataModel::ItemProperty::PropertyType::String, {}, + [&](const std::shared_ptr &var) { return DataModel::PropertyValue(var->value().asString()); }, + [&](std::shared_ptr &var, const DataModel::PropertyValue &newValue) { // Need to check type (int vs double) auto isFloatingPoint = false; - auto value = DataModelBase::asString(newValue); + auto value = DataModel::propertyAsString(newValue); if (!DissolveSys::isNumber(value, isFloatingPoint)) return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", value, var->baseName()); @@ -65,7 +65,7 @@ std::vector> &ExpressionVariableVectorKeywor const std::vector> &ExpressionVariableVectorKeyword::data() const { return data_; } // Return data model -DataTableModel> &ExpressionVariableVectorKeyword::dataModel() { return dataModel_; } +DataModel::Table> &ExpressionVariableVectorKeyword::dataModel() { return dataModel_; } // Return parent ProcedureNode ProcedureNode *ExpressionVariableVectorKeyword::parentNode() { return parentNode_; } diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index 7790a1f206..8886e48508 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -5,7 +5,7 @@ #include "expression/node.h" #include "keywords/base.h" -#include "templates/dataModel.h" +#include "templates/dataModelTable.h" // Forward Declarations class ExpressionVariable; @@ -24,7 +24,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase private: // Reference to vector of data std::vector> &data_; - DataTableModel> dataModel_; + DataModel::Table> dataModel_; // Parent ProcedureNode ProcedureNode *parentNode_; @@ -35,12 +35,12 @@ class ExpressionVariableVectorKeyword : public KeywordBase std::vector> &data(); const std::vector> &data() const; // Return data model - DataTableModel> &dataModel(); + DataModel::Table> &dataModel(); // Return parent ProcedureNode ProcedureNode *parentNode(); const ProcedureNode *parentNode() const; - /* + /*c * Arguments */ public: diff --git a/src/templates/dataModel.h b/src/templates/dataModel.h deleted file mode 100644 index 432dc2211e..0000000000 --- a/src/templates/dataModel.h +++ /dev/null @@ -1,303 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// Copyright (c) 2024 Team Dissolve and contributors - -#pragma once - -#include "templates/flags.h" -#include -#include -#include -#include -#include -#include -#include - -// Index / Column ID, Name / Column Title, Data Type, ReadOnly? -class DataItemProperty -{ - public: - // Data property types - enum class PropertyType - { - Integer, - Double, - String - }; - - // Data property flags - enum PropertyFlag - { - ReadOnly, - Disabled - }; - DataItemProperty(std::string_view name, PropertyType type, Flags flags = {}) - : name_{name}, type_(type), flags_(flags) - { - } - - private: - // Property name - std::string name_; - // Property type - PropertyType type_; - // Property flags - Flags flags_; - - public: - // Return property name - const std::string &name() const { return name_; } - // Return property type - PropertyType type() const { return type_; } - // Return property flags - const Flags &flags() const { return flags_; } -}; - -// Helper type for PropertyValue visitor -template struct DataItemVisitor : Ts... -{ - using Ts::operator()...; -}; -// Explicit deduction guide for PropertyValue visitor -template DataItemVisitor(Ts...) -> DataItemVisitor; - -/* - * DataModelBase - An abstract class intended to allow our custom C++ data classes to be accessed in a consistent - * and simplified way through Qt's Model-View classes, without the need for a specific Qt model for each. - */ -class DataModelBase -{ - public: - DataModelBase() {} - - /* - * Property Values - */ - public: - // Property value variant - using PropertyValue = std::variant; - // Return value as string - static std::string asString(const PropertyValue &value) - { - if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else - return std::get(value); - } - - /* - * Properties - */ - protected: - // Descriptions of relevant item properties within a single object in the container - std::vector itemProperties_; - - public: - // Add item property - void addItemProperty(std::string_view name, DataItemProperty::PropertyType type, - Flags flags = {}) - { - itemProperties_.emplace_back(name, type, flags); - } - - // Return name of specified property - std::string propertyName(int propertyIndex) { return itemProperties_[propertyIndex].name(); } - // Return property type for the specified property - DataItemProperty::PropertyType propertyType(int propertyIndex) { return itemProperties_[propertyIndex].type(); } - // Return all flags set for specified property - const Flags &propertyFlags(int propertyIndex) - { - return itemProperties_[propertyIndex].flags(); - } - // Return whether the specified property flag is set - bool isPropertyFlagSet(int propertyIndex, DataItemProperty::PropertyFlag flag) - { - return itemProperties_[propertyIndex].flags().isSet(flag); - } - - public: - // Return number of data items (i.e. rows) for the specified index - virtual int nDataItems() = 0; - // Return number of properties (i.e. columns) for the specified index - virtual int nProperties() = 0; - // Set property function - virtual bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) = 0; - // Get property function - virtual PropertyValue getProperty(int dataIndex, int propertyIndex) = 0; - - /* - * Item Management - */ - public: - enum class MutationSignal - { - PropertyChanged, - DataCreationStarted, - DataCreationFinished, - DataRemovalStarted, - DataRemovalFinished - }; - using DataMutationSignalFunction = std::function; - DataMutationSignalFunction mutationSignalFunction_ = {}; - void setMutationSignalFunction(DataMutationSignalFunction mutationSignalFunction) - { - mutationSignalFunction_ = std::move(mutationSignalFunction); - } - void emitMutationSignal(MutationSignal signal, int startIndex = 0, int endIndex = 0) - { - if (mutationSignalFunction_) - mutationSignalFunction_(signal, startIndex, endIndex); - } - // Create new item(s) starting at specified vector index - virtual void createItems(int index, int count) = 0; - // Append new item(s) to the end of the data - virtual void appendItems(int count) = 0; - // Remove item(s) starting at specified vector index - virtual void removeItems(int index, int count) = 0; -}; - -template class DataTableModel : public DataModelBase -{ - public: - // Property get function - using PropertyGetFunction = std::function; - // Property set function - using PropertySetFunction = std::function; - - DataTableModel(std::vector &data) : DataModelBase(), data_(data) {} - - /* - * Target Data and Functions - */ - private: - // Target data for the model - std::vector &data_; - // Map of named properties to data getters - std::map getters_; - // Map of named properties to data setters - std::map setters_; - - public: - // Add item property for use in the model - void addProperty(const std::string &name, DataItemProperty::PropertyType type, Flags flags, - PropertyGetFunction getter, PropertySetFunction setter = {}) - { - // Add the property base info - the order will be reflected in the table model - addItemProperty(name, type, flags); - - // Store functions - getters_[name] = std::move(getter); - if (setter) - setters_[name] = std::move(setter); - } - - /* - * Extent - */ - private: - // Return whether the supplied index is valid - bool isIndexValid(int dataIndex, int propertyIndex) const - { - return dataIndex >= 0 && dataIndex < data_.size() && propertyIndex >= 0 && propertyIndex < itemProperties_.size(); - } - - public: - // Return number of data items (rows) in the table - int nDataItems() final { return data_.size(); } - // Return number of properties per data item (i.e. columns) in the table - int nProperties() final { return itemProperties_.size(); } - - /* - * Data Access - */ - public: - // Set property - bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) final - { - // Check index validity - if (!isIndexValid(dataIndex, propertyIndex)) - return false; - - if (itemProperties_[propertyIndex].flags().isSet(DataItemProperty::PropertyFlag::ReadOnly)) - { - fmt::print("Refusing to set data '{}' since it is read-only.\n", itemProperties_[propertyIndex].name()); - return false; - } - - // Set the child at the specified index - if (setters_.find(itemProperties_[propertyIndex].name()) == setters_.end()) - return false; - else - return setters_[itemProperties_[propertyIndex].name()](data_[dataIndex], newValue); - } - // Get property - PropertyValue getProperty(int dataIndex, int propertyIndex) final - { - // Check index validity - if (!isIndexValid(dataIndex, propertyIndex)) - return {}; - - return getters_[itemProperties_[propertyIndex].name()](data_[dataIndex]); - } - - /* - * Item Management - */ - private: - // Item creation function (if required) otherwise default constructor T() will be called - using CreateItemFunction = std::function)>; - CreateItemFunction createItemFunction_; - // Item removal function (if required) - using RemoveItemFunction = std::function; - RemoveItemFunction removeItemFunction_; - - public: - // Set data creation function - void setDataCreationFunction(CreateItemFunction function) { createItemFunction_ = std::move(function); } - // Set data removal function - void setDataRemovalFunction(RemoveItemFunction function) { removeItemFunction_ = std::move(function); } - - public: - // Create new item(s) starting at specified vector index - void createItems(int index, int count) final - { - emitMutationSignal(DataModelBase::MutationSignal::DataCreationStarted, index, index + count - 1); - for (auto n = 0; n < count; ++n) - { - if (createItemFunction_) - createItemFunction_(index + n); - else - data_.insert(data_.begin() + index, DataItem()); - } - emitMutationSignal(DataModelBase::MutationSignal::DataCreationFinished); - } - // Append new item(s) to the end of the data - void appendItems(int count) final - { - emitMutationSignal(DataModelBase::MutationSignal::DataCreationStarted, data_.size(), data_.size() + count - 1); - for (auto n = 0; n < count; ++n) - { - if (createItemFunction_) - createItemFunction_({}); - else - data_.emplace_back(DataItem()); - } - emitMutationSignal(DataModelBase::MutationSignal::DataCreationFinished); - } - // Remove item(s) starting at specified vector index - void removeItems(int index, int count) final - { - emitMutationSignal(DataModelBase::MutationSignal::DataRemovalStarted, index, index + count - 1); - if (removeItemFunction_) - { - for (auto n = 0; n < count; ++n) - removeItemFunction_(index); - } - else - data_.erase(data_.begin() + index, data_.begin() + index + count); - emitMutationSignal(DataModelBase::MutationSignal::DataRemovalFinished); - } -}; diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h new file mode 100644 index 0000000000..c368b6f949 --- /dev/null +++ b/src/templates/dataModelBase.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "templates/dataModelItem.h" +#include +#include +#include +#include +#include +#include + +namespace DataModel +{ +/* + * DataModel::Base - An abstract class intended to allow our custom C++ data classes to be accessed in a consistent + * and simplified way through Qt's Model-View classes, without the need for a specific Qt model for each. + */ +class Base +{ + public: + Base() {} + + /* + * Properties + */ + protected: + // Descriptions of relevant item properties within a single object in the container + std::vector itemProperties_; + + public: + // Add item property + void addItemProperty(std::string_view name, ItemProperty::PropertyType type, Flags flags = {}) + { + itemProperties_.emplace_back(name, type, flags); + } + + // Return name of specified property + std::string propertyName(int propertyIndex) { return itemProperties_[propertyIndex].name(); } + // Return property type for the specified property + ItemProperty::PropertyType propertyType(int propertyIndex) { return itemProperties_[propertyIndex].type(); } + // Return all flags set for specified property + const Flags &propertyFlags(int propertyIndex) { return itemProperties_[propertyIndex].flags(); } + // Return whether the specified property flag is set + bool isPropertyFlagSet(int propertyIndex, ItemProperty::PropertyFlag flag) + { + return itemProperties_[propertyIndex].flags().isSet(flag); + } + + public: + // Return number of data items (i.e. rows) for the specified index + virtual int nDataItems() = 0; + // Return number of properties (i.e. columns) for the specified index + virtual int nProperties() = 0; + // Set property function + virtual bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) = 0; + // Get property function + virtual PropertyValue getProperty(int dataIndex, int propertyIndex) = 0; + + /* + * Item Management + */ + public: + enum class MutationSignal + { + PropertyChanged, + DataCreationStarted, + DataCreationFinished, + DataRemovalStarted, + DataRemovalFinished + }; + using DataMutationSignalFunction = std::function; + DataMutationSignalFunction mutationSignalFunction_ = {}; + void setMutationSignalFunction(DataMutationSignalFunction mutationSignalFunction) + { + mutationSignalFunction_ = std::move(mutationSignalFunction); + } + void emitMutationSignal(MutationSignal signal, int startIndex = 0, int endIndex = 0) + { + if (mutationSignalFunction_) + mutationSignalFunction_(signal, startIndex, endIndex); + } + // Create new item(s) starting at specified vector index + virtual void createItems(int index, int count) = 0; + // Append new item(s) to the end of the data + virtual void appendItems(int count) = 0; + // Remove item(s) starting at specified vector index + virtual void removeItems(int index, int count) = 0; +}; +} // namespace DataModel diff --git a/src/templates/dataModelItem.h b/src/templates/dataModelItem.h new file mode 100644 index 0000000000..5f449872bb --- /dev/null +++ b/src/templates/dataModelItem.h @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "templates/flags.h" +#include +#include +#include + +namespace DataModel +{ +// Property for an Item within a DataModel +class ItemProperty +{ + public: + // Data property types + enum class PropertyType + { + Integer, + Double, + String + }; + + // Data property flags + enum PropertyFlag + { + ReadOnly, + Disabled + }; + ItemProperty(std::string_view name, PropertyType type, Flags flags = {}) + : name_{name}, type_(type), flags_(flags) + { + } + + private: + // Property name + std::string name_; + // Property type + PropertyType type_; + // Property flags + Flags flags_; + + public: + // Return property name + const std::string &name() const { return name_; } + // Return property type + PropertyType type() const { return type_; } + // Return property flags + const Flags &flags() const { return flags_; } +}; + +// Property value variant +using PropertyValue = std::variant; +// Return value as string +static std::string propertyAsString(const PropertyValue &value) +{ + if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else if (std::holds_alternative(value)) + return fmt::format("{}", std::get(value)); + else + return std::get(value); +} + +// Helper type for PropertyValue visitor +template struct PropertyVisitor : Ts... +{ + using Ts::operator()...; +}; +// Explicit deduction guide for PropertyValue visitor +template PropertyVisitor(Ts...) -> PropertyVisitor; +}; // namespace DataModel diff --git a/src/templates/dataModelTable.h b/src/templates/dataModelTable.h new file mode 100644 index 0000000000..1b471845b8 --- /dev/null +++ b/src/templates/dataModelTable.h @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "templates/dataModelBase.h" + +namespace DataModel +{ +// Table Model for Data +template class Table : public Base +{ + public: + // Property get function + using PropertyGetFunction = std::function; + // Property set function + using PropertySetFunction = std::function; + + Table(std::vector &data) : Base(), data_(data) {} + + /* + * Target Data and Functions + */ + private: + // Target data for the model + std::vector &data_; + // Map of named properties to data getters + std::map getters_; + // Map of named properties to data setters + std::map setters_; + + public: + // Add item property for use in the model + void addProperty(const std::string &name, ItemProperty::PropertyType type, Flags flags, + PropertyGetFunction getter, PropertySetFunction setter = {}) + { + // Add the property base info - the order will be reflected in the table model + addItemProperty(name, type, flags); + + // Store functions + getters_[name] = std::move(getter); + if (setter) + setters_[name] = std::move(setter); + } + + /* + * Extent + */ + private: + // Return whether the supplied index is valid + bool isIndexValid(int dataIndex, int propertyIndex) const + { + return dataIndex >= 0 && dataIndex < data_.size() && propertyIndex >= 0 && propertyIndex < itemProperties_.size(); + } + + public: + // Return number of data items (rows) in the table + int nDataItems() final { return data_.size(); } + // Return number of properties per data item (i.e. columns) in the table + int nProperties() final { return itemProperties_.size(); } + + /* + * Data Access + */ + public: + // Set property + bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) final + { + // Check index validity + if (!isIndexValid(dataIndex, propertyIndex)) + return false; + + if (itemProperties_[propertyIndex].flags().isSet(ItemProperty::PropertyFlag::ReadOnly)) + { + fmt::print("Refusing to set data '{}' since it is read-only.\n", itemProperties_[propertyIndex].name()); + return false; + } + + // Set the child at the specified index + if (setters_.find(itemProperties_[propertyIndex].name()) == setters_.end()) + return false; + else + return setters_[itemProperties_[propertyIndex].name()](data_[dataIndex], newValue); + } + // Get property + PropertyValue getProperty(int dataIndex, int propertyIndex) final + { + // Check index validity + if (!isIndexValid(dataIndex, propertyIndex)) + return {}; + + return getters_[itemProperties_[propertyIndex].name()](data_[dataIndex]); + } + + /* + * Item Management + */ + private: + // Item creation function (if required) otherwise default constructor T() will be called + using CreateItemFunction = std::function)>; + CreateItemFunction createItemFunction_; + // Item removal function (if required) + using RemoveItemFunction = std::function; + RemoveItemFunction removeItemFunction_; + + public: + // Set data creation function + void setDataCreationFunction(CreateItemFunction function) { createItemFunction_ = std::move(function); } + // Set data removal function + void setDataRemovalFunction(RemoveItemFunction function) { removeItemFunction_ = std::move(function); } + + public: + // Create new item(s) starting at specified vector index + void createItems(int index, int count) final + { + emitMutationSignal(Base::MutationSignal::DataCreationStarted, index, index + count - 1); + for (auto n = 0; n < count; ++n) + { + if (createItemFunction_) + createItemFunction_(index + n); + else + data_.insert(data_.begin() + index, DataItem()); + } + emitMutationSignal(Base::MutationSignal::DataCreationFinished); + } + // Append new item(s) to the end of the data + void appendItems(int count) final + { + emitMutationSignal(Base::MutationSignal::DataCreationStarted, data_.size(), data_.size() + count - 1); + for (auto n = 0; n < count; ++n) + { + if (createItemFunction_) + createItemFunction_({}); + else + data_.emplace_back(DataItem()); + } + emitMutationSignal(Base::MutationSignal::DataCreationFinished); + } + // Remove item(s) starting at specified vector index + void removeItems(int index, int count) final + { + emitMutationSignal(Base::MutationSignal::DataRemovalStarted, index, index + count - 1); + if (removeItemFunction_) + { + for (auto n = 0; n < count; ++n) + removeItemFunction_(index); + } + else + data_.erase(data_.begin() + index, data_.begin() + index + count); + emitMutationSignal(Base::MutationSignal::DataRemovalFinished); + } +}; +} // namespace DataModel From c2cd9ca65333527aac69bd0dbe7cadc9d224708d Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 19 Jul 2024 16:07:46 +0100 Subject: [PATCH 20/42] Give both base and storage classes to model to try and handle different storage approaches. --- src/expression/variable.cpp | 47 ++++++++++++++++++ src/expression/variable.h | 15 +++++- src/keywords/expressionVariableVector.cpp | 41 +++------------- src/keywords/expressionVariableVector.h | 4 +- src/templates/dataModelBase.h | 12 ++--- src/templates/dataModelItem.h | 17 +++++++ src/templates/dataModelTable.h | 60 +++++++++++++++++------ 7 files changed, 134 insertions(+), 62 deletions(-) diff --git a/src/expression/variable.cpp b/src/expression/variable.cpp index af71224aa7..dea4b68cdb 100644 --- a/src/expression/variable.cpp +++ b/src/expression/variable.cpp @@ -3,6 +3,7 @@ #include "expression/variable.h" #include "base/messenger.h" +#include "base/sysFunc.h" #include ExpressionVariable::ExpressionVariable(const ExpressionValue &value) @@ -58,6 +59,10 @@ const ExpressionValue &ExpressionVariable::value() const { return value_; } // Return pointer to value ExpressionValue *ExpressionVariable::valuePointer() { return &value_; } +/* + * I/O + */ + // Express as a serialisable value SerialisedValue ExpressionVariable::serialise() const { return {{"name", baseName_}, {"value", value_}}; } @@ -67,3 +72,45 @@ void ExpressionVariable::deserialise(const SerialisedValue &node) value_ = toml::find(node, "value"); setBaseName(toml::find(node, "name")); } + +/* + * Modelable + */ + +// Return property getters and basic setters (if relevant) +const std::vector::ModelableProperty> ExpressionVariable::modelableProperties() +{ + return {{"Name", + DataModel::ItemProperty::PropertyType::String, + {}, + [&](const ExpressionVariable *var) { return DataModel::PropertyValue(var->baseName()); }, + [&](ExpressionVariable *var, const DataModel::PropertyValue &newValue) + { + var->setBaseName(DataModel::propertyAsString(newValue)); + return true; + }}, + {"Type", + DataModel::ItemProperty::PropertyType::String, + {DataModel::ItemProperty::PropertyFlag::ReadOnly}, + [&](const ExpressionVariable *var) { + return DataModel::PropertyValue( + std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); + }, + [&](ExpressionVariable *var, const DataModel::PropertyValue &newValue) { return false; }}, + {"Value", + DataModel::ItemProperty::PropertyType::String, + {}, + [&](const ExpressionVariable *var) { return DataModel::PropertyValue(var->value().asString()); }, + [&](ExpressionVariable *var, const DataModel::PropertyValue &newValue) + { + // Need to check type (int vs double) + auto isFloatingPoint = false; + auto value = DataModel::propertyAsString(newValue); + if (!DissolveSys::isNumber(value, isFloatingPoint)) + return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", value, + var->baseName()); + + isFloatingPoint ? var->setValue(std::stod(value)) : var->setValue(std::stoi(value)); + return true; + }}}; +} diff --git a/src/expression/variable.h b/src/expression/variable.h index e144c24bb2..7205941908 100644 --- a/src/expression/variable.h +++ b/src/expression/variable.h @@ -4,9 +4,10 @@ #pragma once #include "expression/value.h" +#include "templates/dataModelItem.h" // Variable -class ExpressionVariable : public Serialisable<> +class ExpressionVariable : public Serialisable<>, public DataModel::Modelable { public: ExpressionVariable(const ExpressionValue &value = ExpressionValue()); @@ -45,8 +46,20 @@ class ExpressionVariable : public Serialisable<> const ExpressionValue &value() const; // Return pointer to value ExpressionValue *valuePointer(); + + /* + * I/O + */ + public: // Express as a serialisable value SerialisedValue serialise() const override; // Read values from a serialisable value void deserialise(const SerialisedValue &node); + + /* + * Modelable + */ + public: + // Return basic property information including getter and setter (if relevant) + static const std::vector modelableProperties(); }; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 9306f9d0bd..6c8b523313 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -10,41 +10,9 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &data, ProcedureNode *parentNode) - : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_) + : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), + dataModel_(data_, ExpressionVariable::modelableProperties()) { - dataModel_.addProperty( - "Name", DataModel::ItemProperty::PropertyType::String, {}, - [&](const std::shared_ptr &var) { return DataModel::PropertyValue(var->baseName()); }, - [&](std::shared_ptr &var, const DataModel::PropertyValue &newValue) - { - // Must check for existing var in scope with the same name - auto p = parentNode_->getParameter(DataModel::propertyAsString(newValue)); - if (p && p != var) - return false; - var->setBaseName(DataModel::propertyAsString(newValue)); - return true; - }); - dataModel_.addProperty("Type", DataModel::ItemProperty::PropertyType::String, - {DataModel::ItemProperty::PropertyFlag::ReadOnly}, - [&](const std::shared_ptr &var) { - return DataModel::PropertyValue( - std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); - }); - dataModel_.addProperty( - "Value", DataModel::ItemProperty::PropertyType::String, {}, - [&](const std::shared_ptr &var) { return DataModel::PropertyValue(var->value().asString()); }, - [&](std::shared_ptr &var, const DataModel::PropertyValue &newValue) - { - // Need to check type (int vs double) - auto isFloatingPoint = false; - auto value = DataModel::propertyAsString(newValue); - if (!DissolveSys::isNumber(value, isFloatingPoint)) - return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", value, - var->baseName()); - - isFloatingPoint ? var->setValue(std::stod(value)) : var->setValue(std::stoi(value)); - return true; - }); dataModel_.setDataCreationFunction( [&](std::optional insertAt) @@ -65,7 +33,10 @@ std::vector> &ExpressionVariableVectorKeywor const std::vector> &ExpressionVariableVectorKeyword::data() const { return data_; } // Return data model -DataModel::Table> &ExpressionVariableVectorKeyword::dataModel() { return dataModel_; } +DataModel::Table> &ExpressionVariableVectorKeyword::dataModel() +{ + return dataModel_; +} // Return parent ProcedureNode ProcedureNode *ExpressionVariableVectorKeyword::parentNode() { return parentNode_; } diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index 8886e48508..493b746b69 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -24,7 +24,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase private: // Reference to vector of data std::vector> &data_; - DataModel::Table> dataModel_; + DataModel::Table> dataModel_; // Parent ProcedureNode ProcedureNode *parentNode_; @@ -35,7 +35,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase std::vector> &data(); const std::vector> &data() const; // Return data model - DataModel::Table> &dataModel(); + DataModel::Table> &dataModel(); // Return parent ProcedureNode ProcedureNode *parentNode(); const ProcedureNode *parentNode() const; diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h index c368b6f949..842de5d0f2 100644 --- a/src/templates/dataModelBase.h +++ b/src/templates/dataModelBase.h @@ -4,12 +4,6 @@ #pragma once #include "templates/dataModelItem.h" -#include -#include -#include -#include -#include -#include namespace DataModel { @@ -29,13 +23,15 @@ class Base // Descriptions of relevant item properties within a single object in the container std::vector itemProperties_; - public: + protected: // Add item property - void addItemProperty(std::string_view name, ItemProperty::PropertyType type, Flags flags = {}) + void addItemProperty(std::string_view name, ItemProperty::PropertyType type, + const Flags &flags = {}) { itemProperties_.emplace_back(name, type, flags); } + public: // Return name of specified property std::string propertyName(int propertyIndex) { return itemProperties_[propertyIndex].name(); } // Return property type for the specified property diff --git a/src/templates/dataModelItem.h b/src/templates/dataModelItem.h index 5f449872bb..a8f96ff002 100644 --- a/src/templates/dataModelItem.h +++ b/src/templates/dataModelItem.h @@ -72,4 +72,21 @@ template struct PropertyVisitor : Ts... }; // Explicit deduction guide for PropertyValue visitor template PropertyVisitor(Ts...) -> PropertyVisitor; + +// Modelable - Provides declarations for use by a class defining its properties +template class Modelable +{ + public: + using ModelableGetter = std::function; + using ModelableSetter = std::function; + using ModelableProperty = std::tuple, + ModelableGetter, ModelableSetter>; + + /* + * The class D should derive from Modelable and subsequently define a static function or similar to return a vector of + * ModelableProperty definitions as they should appear in a model. + */ + // Return basic property information including getter and setter (if relevant) + // virtual const std::vector modelableProperties(); +}; }; // namespace DataModel diff --git a/src/templates/dataModelTable.h b/src/templates/dataModelTable.h index 1b471845b8..409875e21e 100644 --- a/src/templates/dataModelTable.h +++ b/src/templates/dataModelTable.h @@ -7,16 +7,25 @@ namespace DataModel { -// Table Model for Data -template class Table : public Base +// Table Model for Data based on std::vector +template class Table : public Base { public: + // Modelable properties + using ModelableProperties = std::vector::ModelableProperty>; // Property get function - using PropertyGetFunction = std::function; + using PropertyGetFunction = std::function; // Property set function - using PropertySetFunction = std::function; + using PropertySetFunction = std::function; - Table(std::vector &data) : Base(), data_(data) {} + Table(std::vector &data, const ModelableProperties &properties) : Base(), data_(data) + { + // Add properties from the modelable target + for (auto &[name, type, flags, getter, setter] : properties) + { + addProperty(name, type, flags, getter, setter); + } + } /* * Target Data and Functions @@ -29,7 +38,7 @@ template class Table : public Base // Map of named properties to data setters std::map setters_; - public: + private: // Add item property for use in the model void addProperty(const std::string &name, ItemProperty::PropertyType type, Flags flags, PropertyGetFunction getter, PropertySetFunction setter = {}) @@ -62,7 +71,35 @@ template class Table : public Base /* * Data Access */ + private: + // Call getter + PropertyValue callGetter(PropertyGetFunction &getter, DataItemClass &item) { return getter(&item); } + PropertyValue callGetter(PropertyGetFunction &getter, std::shared_ptr &item) { return getter(item.get()); } + PropertyValue callGetter(PropertyGetFunction &getter, std::unique_ptr &item) { return getter(item.get()); } + // Call setter + bool callSetter(PropertySetFunction &setter, DataItemClass &item, const PropertyValue &newValue) + { + return setter(&item, newValue); + } + bool callSetter(PropertySetFunction &setter, std::shared_ptr &item, const PropertyValue &newValue) + { + return setter(item.get(), newValue); + } + bool callSetter(PropertySetFunction &setter, std::unique_ptr &item, const PropertyValue &newValue) + { + return setter(item.get(), newValue); + } + public: + // Get property + PropertyValue getProperty(int dataIndex, int propertyIndex) final + { + // Check index validity + if (!isIndexValid(dataIndex, propertyIndex)) + return {}; + + return callGetter(getters_[itemProperties_[propertyIndex].name()], data_[dataIndex]); + } // Set property bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) final { @@ -80,16 +117,7 @@ template class Table : public Base if (setters_.find(itemProperties_[propertyIndex].name()) == setters_.end()) return false; else - return setters_[itemProperties_[propertyIndex].name()](data_[dataIndex], newValue); - } - // Get property - PropertyValue getProperty(int dataIndex, int propertyIndex) final - { - // Check index validity - if (!isIndexValid(dataIndex, propertyIndex)) - return {}; - - return getters_[itemProperties_[propertyIndex].name()](data_[dataIndex]); + return callSetter(setters_[itemProperties_[propertyIndex].name()], data_[dataIndex], newValue); } /* From 5f4a2d4a0293e75252b29864b0a997845e250a3b Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 19 Jul 2024 16:13:21 +0100 Subject: [PATCH 21/42] Allow setters to be overriden for specific purposes. --- src/keywords/expressionVariableVector.cpp | 10 ++++++++++ src/templates/dataModelTable.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 6c8b523313..e34fcd9519 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -13,6 +13,16 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vectorgetParameter(DataModel::propertyAsString(newValue)); + if (p && p.get() != var) + return false; + var->setBaseName(DataModel::propertyAsString(newValue)); + return true; + }); dataModel_.setDataCreationFunction( [&](std::optional insertAt) diff --git a/src/templates/dataModelTable.h b/src/templates/dataModelTable.h index 409875e21e..ca4b606cf5 100644 --- a/src/templates/dataModelTable.h +++ b/src/templates/dataModelTable.h @@ -52,6 +52,10 @@ template class Table : public Base setters_[name] = std::move(setter); } + public: + // Add or override setter for specified property + void setSetter(std::string name, PropertySetFunction setter) { setters_[name] = std::move(setter); } + /* * Extent */ From dc7c7d7d866166136244e7855da8b014bd656418 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 19 Jul 2024 16:15:35 +0100 Subject: [PATCH 22/42] Rename functions for consistency. --- src/keywords/expressionVariableVector.cpp | 2 +- src/templates/dataModelTable.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index e34fcd9519..e8ea796710 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -24,7 +24,7 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector insertAt) { auto allParameters = parentNode_->getParametersInScope(); diff --git a/src/templates/dataModelTable.h b/src/templates/dataModelTable.h index ca4b606cf5..1707a0b7b4 100644 --- a/src/templates/dataModelTable.h +++ b/src/templates/dataModelTable.h @@ -137,9 +137,9 @@ template class Table : public Base public: // Set data creation function - void setDataCreationFunction(CreateItemFunction function) { createItemFunction_ = std::move(function); } + void setCreator(CreateItemFunction function) { createItemFunction_ = std::move(function); } // Set data removal function - void setDataRemovalFunction(RemoveItemFunction function) { removeItemFunction_ = std::move(function); } + void setRemover(RemoveItemFunction function) { removeItemFunction_ = std::move(function); } public: // Create new item(s) starting at specified vector index From 83ea61ad48013f75eed0295537cd23810125b98d Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 19 Jul 2024 16:26:59 +0100 Subject: [PATCH 23/42] Use a static templated function specialisation to provide the properties of the target class. --- src/expression/variable.cpp | 4 +++- src/expression/variable.h | 7 ------- src/keywords/expressionVariableVector.cpp | 3 +-- src/templates/dataModelItem.h | 7 ++++--- src/templates/dataModelTable.h | 6 +++--- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/expression/variable.cpp b/src/expression/variable.cpp index dea4b68cdb..ba7f7259e0 100644 --- a/src/expression/variable.cpp +++ b/src/expression/variable.cpp @@ -78,7 +78,9 @@ void ExpressionVariable::deserialise(const SerialisedValue &node) */ // Return property getters and basic setters (if relevant) -const std::vector::ModelableProperty> ExpressionVariable::modelableProperties() +template <> +const std::vector::ModelableProperty> +DataModel::Modelable::modelableProperties() { return {{"Name", DataModel::ItemProperty::PropertyType::String, diff --git a/src/expression/variable.h b/src/expression/variable.h index 7205941908..f2c5d2900a 100644 --- a/src/expression/variable.h +++ b/src/expression/variable.h @@ -55,11 +55,4 @@ class ExpressionVariable : public Serialisable<>, public DataModel::Modelable modelableProperties(); }; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index e8ea796710..7748b063c4 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -10,8 +10,7 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &data, ProcedureNode *parentNode) - : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), - dataModel_(data_, ExpressionVariable::modelableProperties()) + : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_) { // Override the setter for the "Name" property in the model as we need to ensure unique naming in the same scope dataModel_.setSetter("Name", diff --git a/src/templates/dataModelItem.h b/src/templates/dataModelItem.h index a8f96ff002..8cb217f09b 100644 --- a/src/templates/dataModelItem.h +++ b/src/templates/dataModelItem.h @@ -83,10 +83,11 @@ template class Modelable ModelableGetter, ModelableSetter>; /* - * The class D should derive from Modelable and subsequently define a static function or similar to return a vector of - * ModelableProperty definitions as they should appear in a model. + * We now define a static templated function to return a vector of ModelableProperty definitions for the target class D. The + * class D needs to provide this function as a templated function specialisation. */ + public: // Return basic property information including getter and setter (if relevant) - // virtual const std::vector modelableProperties(); + static const std::vector modelableProperties(); }; }; // namespace DataModel diff --git a/src/templates/dataModelTable.h b/src/templates/dataModelTable.h index 1707a0b7b4..0cbc7a9dfe 100644 --- a/src/templates/dataModelTable.h +++ b/src/templates/dataModelTable.h @@ -18,10 +18,10 @@ template class Table : public Base // Property set function using PropertySetFunction = std::function; - Table(std::vector &data, const ModelableProperties &properties) : Base(), data_(data) + Table(std::vector &data) : Base(), data_(data) { - // Add properties from the modelable target - for (auto &[name, type, flags, getter, setter] : properties) + // Add properties from the modelable base class + for (auto &[name, type, flags, getter, setter] : Modelable::modelableProperties()) { addProperty(name, type, flags, getter, setter); } From e3b82cfb883fc5cfa2fa67ca1b48521e4fcbe70a Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Sat, 20 Jul 2024 15:09:23 +0100 Subject: [PATCH 24/42] Rename Qt interface for consistency. --- src/gui/CMakeLists.txt | 4 +-- ...erface.cpp => dataModelTableInterface.cpp} | 29 ++++++++++--------- ...lInterface.h => dataModelTableInterface.h} | 6 ++-- .../keywordWidgets/expressionVariableVector.h | 4 +-- 4 files changed, 23 insertions(+), 20 deletions(-) rename src/gui/{dataTableModelInterface.cpp => dataModelTableInterface.cpp} (81%) rename src/gui/{dataTableModelInterface.h => dataModelTableInterface.h} (90%) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e005bc817e..99c0dc44bb 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -19,8 +19,8 @@ add_library( configurationViewerWidget.cpp configurationViewerWidget.h configurationViewerWidget.ui - dataTableModelInterface.cpp - dataTableModelInterface.h + dataModelTableInterface.cpp + dataModelTableInterface.h dataViewer.cpp dataViewer_input.cpp dataViewer_interaction.cpp diff --git a/src/gui/dataTableModelInterface.cpp b/src/gui/dataModelTableInterface.cpp similarity index 81% rename from src/gui/dataTableModelInterface.cpp rename to src/gui/dataModelTableInterface.cpp index 18d0614b6b..dd176467f5 100644 --- a/src/gui/dataTableModelInterface.cpp +++ b/src/gui/dataModelTableInterface.cpp @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors -#include "gui/dataTableModelInterface.h" +#include "gui/dataModelTableInterface.h" #include "procedure/nodes/node.h" -DataTableModelInterface::DataTableModelInterface(DataModel::Base &dataModel) : dataModel_(dataModel) +DataModelTableInterface::DataModelTableInterface(DataModel::Base &dataModel) : dataModel_(dataModel) { dataModel_.setMutationSignalFunction([this](DataModel::Base::MutationSignal signal, int startIndex, int endIndex) { dataMutated(signal, startIndex, endIndex); }); @@ -15,13 +15,13 @@ DataTableModelInterface::DataTableModelInterface(DataModel::Base &dataModel) : d */ // Return row count -int DataTableModelInterface::rowCount(const QModelIndex &parent) const { return dataModel_.nDataItems(); } +int DataModelTableInterface::rowCount(const QModelIndex &parent) const { return dataModel_.nDataItems(); } // Return column count -int DataTableModelInterface::columnCount(const QModelIndex &parent) const { return dataModel_.nProperties(); } +int DataModelTableInterface::columnCount(const QModelIndex &parent) const { return dataModel_.nProperties(); } // Return flags for the specified model index -Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const +Qt::ItemFlags DataModelTableInterface::flags(const QModelIndex &index) const { auto &propertyFlags = dataModel_.propertyFlags(index.column()); Qt::ItemFlags flags = Qt::ItemIsSelectable; @@ -33,7 +33,7 @@ Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const } // Return header data for the specified section, orientation, and role -QVariant DataTableModelInterface::headerData(int section, Qt::Orientation orientation, int role) const +QVariant DataModelTableInterface::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return {}; @@ -42,7 +42,7 @@ QVariant DataTableModelInterface::headerData(int section, Qt::Orientation orient } // Return data for the index and role specified -QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const +QVariant DataModelTableInterface::data(const QModelIndex &index, int role) const { if (role != Qt::DisplayRole && role != Qt::EditRole) return {}; @@ -59,7 +59,7 @@ QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const } // Set data for the index and role specified -bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant &value, int role) +bool DataModelTableInterface::setData(const QModelIndex &index, const QVariant &value, int role) { if (role != Qt::EditRole || dataModel_.isPropertyFlagSet(index.column(), DataModel::ItemProperty::PropertyFlag::ReadOnly)) return false; @@ -79,7 +79,7 @@ bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant & dataModel_.setProperty(index.row(), index.column(), DataModel::PropertyValue(value.toString().toStdString())); break; default: - Messenger::error("DataTableModelInterface doesn't know how to handle this PropertyType.\n"); + Messenger::error("DataModelTableInterface doesn't know how to handle this PropertyType.\n"); break; } @@ -90,7 +90,7 @@ bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant & } // Insert one or more rows at the specified position -bool DataTableModelInterface::insertRows(int row, int count, const QModelIndex &parent) +bool DataModelTableInterface::insertRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); @@ -100,7 +100,7 @@ bool DataTableModelInterface::insertRows(int row, int count, const QModelIndex & } // Append one or more rows -bool DataTableModelInterface::appendRows(int count, const QModelIndex &parent) +bool DataModelTableInterface::appendRows(int count, const QModelIndex &parent) { Q_UNUSED(parent); @@ -110,7 +110,7 @@ bool DataTableModelInterface::appendRows(int count, const QModelIndex &parent) } // Remove one or more rows starting from the specified position -bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex &parent) +bool DataModelTableInterface::removeRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); @@ -124,7 +124,7 @@ bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex & */ // React to a mutation in the model -void DataTableModelInterface::dataMutated(DataModel::Base::MutationSignal signal, int startIndex, int endIndex) +void DataModelTableInterface::dataMutated(DataModel::Base::MutationSignal signal, int startIndex, int endIndex) { switch (signal) { @@ -140,5 +140,8 @@ void DataTableModelInterface::dataMutated(DataModel::Base::MutationSignal signal case (DataModel::Base::MutationSignal::DataRemovalFinished): endRemoveRows(); break; + default: + Messenger::error("DataModelTableInterface::dataMutated() was passed a signal it didn't recognise...\n"); + break; } } diff --git a/src/gui/dataTableModelInterface.h b/src/gui/dataModelTableInterface.h similarity index 90% rename from src/gui/dataTableModelInterface.h rename to src/gui/dataModelTableInterface.h index 0cb141d69c..f33c477215 100644 --- a/src/gui/dataTableModelInterface.h +++ b/src/gui/dataModelTableInterface.h @@ -7,11 +7,11 @@ #include #include -// QAbstractTableModel Interface to DataTableModel -class DataTableModelInterface : public QAbstractTableModel +// QAbstractTableModel Interface to DataModel::Table +class DataModelTableInterface : public QAbstractTableModel { public: - DataTableModelInterface(DataModel::Base &dataModel); + DataModelTableInterface(DataModel::Base &dataModel); private: // Model with which to interface diff --git a/src/gui/keywordWidgets/expressionVariableVector.h b/src/gui/keywordWidgets/expressionVariableVector.h index 82d2992955..a599e1fb70 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.h +++ b/src/gui/keywordWidgets/expressionVariableVector.h @@ -3,7 +3,7 @@ #pragma once -#include "gui/dataTableModelInterface.h" +#include "gui/dataModelTableInterface.h" #include "gui/keywordWidgets/base.h" #include "gui/keywordWidgets/ui_expressionVariableVector.h" #include "keywords/expressionVariableVector.h" @@ -30,7 +30,7 @@ class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidg // Main form declaration Ui::ExpressionVariableVectorWidget ui_; // Model for table - DataTableModelInterface variableModel_; + DataModelTableInterface variableModel_; private Q_SLOTS: void variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); From a6b72223071eda5960c19c99387f9d3683e4d53f Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Sat, 20 Jul 2024 15:15:12 +0100 Subject: [PATCH 25/42] Tidy up base model. --- src/templates/dataModelBase.h | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h index 842de5d0f2..1721be0774 100644 --- a/src/templates/dataModelBase.h +++ b/src/templates/dataModelBase.h @@ -58,6 +58,18 @@ class Base * Item Management */ public: + // Create new item(s) starting at specified vector index + virtual void createItems(int index, int count) = 0; + // Append new item(s) to the end of the data + virtual void appendItems(int count) = 0; + // Remove item(s) starting at specified vector index + virtual void removeItems(int index, int count) = 0; + + /* + * Signalling + */ + public: + // Mutation Signals enum class MutationSignal { PropertyChanged, @@ -66,22 +78,23 @@ class Base DataRemovalStarted, DataRemovalFinished }; + + private: + // Mutation signal function used to notify an associated Qt model using DataMutationSignalFunction = std::function; DataMutationSignalFunction mutationSignalFunction_ = {}; + + public: + // Set mutation signal function void setMutationSignalFunction(DataMutationSignalFunction mutationSignalFunction) { mutationSignalFunction_ = std::move(mutationSignalFunction); } + // Emit mutation signal void emitMutationSignal(MutationSignal signal, int startIndex = 0, int endIndex = 0) { if (mutationSignalFunction_) mutationSignalFunction_(signal, startIndex, endIndex); } - // Create new item(s) starting at specified vector index - virtual void createItems(int index, int count) = 0; - // Append new item(s) to the end of the data - virtual void appendItems(int count) = 0; - // Remove item(s) starting at specified vector index - virtual void removeItems(int index, int count) = 0; }; } // namespace DataModel From 75cbe14e904626a1e574fa869896c5faf7595a73 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Sun, 21 Jul 2024 11:44:06 +0100 Subject: [PATCH 26/42] Sneaky overloading of an item creation function to handle bare vs pointer classes. --- .../expressionVariableVector.cpp | 2 +- src/keywords/expressionVariableVector.cpp | 47 ++++---- src/keywords/expressionVariableVector.h | 12 +-- src/procedure/nodes/node.cpp | 7 +- src/procedure/nodes/node.h | 5 +- src/templates/dataModelBase.h | 2 + src/templates/dataModelTable.h | 100 ++++++++++++++---- 7 files changed, 118 insertions(+), 57 deletions(-) diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index 8fa2f7327a..896b541271 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -9,7 +9,7 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWidget *parent, ExpressionVariableVectorKeyword *keyword, const CoreData &coreData) - : QWidget(parent), KeywordWidgetBase(coreData), keyword_(keyword), variableModel_(keyword->dataModel()) + : QWidget(parent), KeywordWidgetBase(coreData), keyword_(keyword), variableModel_(keyword->data()) { // Create and set up the UI for our widget ui_.setupUi(this); diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 7748b063c4..57097760d6 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -8,28 +8,27 @@ #include "procedure/nodes/node.h" #include -ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &data, - ProcedureNode *parentNode) - : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_) +ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword( + DataModel::Table> &data, ProcedureNode *parentNode) + : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode) { // Override the setter for the "Name" property in the model as we need to ensure unique naming in the same scope - dataModel_.setSetter("Name", - [&](ExpressionVariable *var, const DataModel::PropertyValue &newValue) - { - auto p = parentNode_->getParameter(DataModel::propertyAsString(newValue)); - if (p && p.get() != var) - return false; - var->setBaseName(DataModel::propertyAsString(newValue)); - return true; - }); - - dataModel_.setCreator( - [&](std::optional insertAt) + data_.setSetter("Name", + [&](ExpressionVariable *var, const DataModel::PropertyValue &newValue) + { + auto p = parentNode_->getParameter(DataModel::propertyAsString(newValue)); + if (p && p.get() != var) + return false; + var->setBaseName(DataModel::propertyAsString(newValue)); + return true; + }); + + data_.setCreator( + [&]() { auto allParameters = parentNode_->getParametersInScope(); return parentNode_->addParameter( - DissolveSys::uniqueName("NewVariable", allParameters, [](const auto &var) { return var->name(); }), {}, - insertAt); + DissolveSys::uniqueName("NewVariable", allParameters, [](const auto &var) { return var->name(); }), {}); }); } @@ -37,14 +36,14 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &ExpressionVariableVectorKeyword::data() { return data_; } -const std::vector> &ExpressionVariableVectorKeyword::data() const { return data_; } - -// Return data model -DataModel::Table> &ExpressionVariableVectorKeyword::dataModel() +// Return reference to data +DataModel::Table> &ExpressionVariableVectorKeyword::data() +{ + return data_; +} +const DataModel::Table> &ExpressionVariableVectorKeyword::data() const { - return dataModel_; + return data_; } // Return parent ProcedureNode diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index 493b746b69..b4e56916d0 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -15,7 +15,8 @@ class ProcedureNode; class ExpressionVariableVectorKeyword : public KeywordBase { public: - ExpressionVariableVectorKeyword(std::vector> &data, ProcedureNode *parentNode); + ExpressionVariableVectorKeyword(DataModel::Table> &data, + ProcedureNode *parentNode); ~ExpressionVariableVectorKeyword() override = default; /* @@ -23,8 +24,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase */ private: // Reference to vector of data - std::vector> &data_; - DataModel::Table> dataModel_; + DataModel::Table> &data_; // Parent ProcedureNode ProcedureNode *parentNode_; @@ -32,10 +32,8 @@ class ExpressionVariableVectorKeyword : public KeywordBase // Has not changed from initial value bool isDefault() const override; // Return reference to vector of data - std::vector> &data(); - const std::vector> &data() const; - // Return data model - DataModel::Table> &dataModel(); + DataModel::Table> &data(); + const DataModel::Table> &data() const; // Return parent ProcedureNode ProcedureNode *parentNode(); const ProcedureNode *parentNode() const; diff --git a/src/procedure/nodes/node.cpp b/src/procedure/nodes/node.cpp index 9be05b3d5b..e78c086c53 100644 --- a/src/procedure/nodes/node.cpp +++ b/src/procedure/nodes/node.cpp @@ -158,10 +158,7 @@ std::shared_ptr ProcedureNode::addParameter(std::string_view std::optional insertAt) { auto newVar = std::make_shared(name, initialValue); - if (insertAt) - parameters_.insert(parameters_.begin() + *insertAt, newVar); - else - parameters_.emplace_back(newVar); + parameters_.insertThis(newVar, insertAt); if (type_ != ProcedureNode::NodeType::Parameters) newVar->setNamePrefix(name_); @@ -195,7 +192,7 @@ std::shared_ptr ProcedureNode::getParameter(std::string_view } // Return references to all parameters for this node -const std::vector> &ProcedureNode::parameters() const { return parameters_; } +const std::vector> &ProcedureNode::parameters() const { return parameters_.data(); } /* * Execution diff --git a/src/procedure/nodes/node.h b/src/procedure/nodes/node.h index 02381419d9..42793df7e8 100644 --- a/src/procedure/nodes/node.h +++ b/src/procedure/nodes/node.h @@ -5,15 +5,16 @@ #include "base/enumOptions.h" #include "base/serialiser.h" +#include "expression/variable.h" #include "keywords/store.h" #include "procedure/nodes/aliases.h" #include "procedure/nodes/context.h" +#include "templates/dataModelTable.h" #include "templates/optionalRef.h" // Forward Declarations class Configuration; class CoreData; -class ExpressionVariable; class GenericList; class LineParser; class ProcedureNodeSequence; @@ -132,7 +133,7 @@ class ProcedureNode : public std::enable_shared_from_this, public */ protected: // Defined parameters - std::vector> parameters_; + DataModel::Table> parameters_; // Set named parameter in supplied vector bool setParameter(std::vector> ¶meters, std::string_view parameter, ExpressionValue value); diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h index 1721be0774..c34efe7bb5 100644 --- a/src/templates/dataModelBase.h +++ b/src/templates/dataModelBase.h @@ -64,6 +64,8 @@ class Base virtual void appendItems(int count) = 0; // Remove item(s) starting at specified vector index virtual void removeItems(int index, int count) = 0; + // Clear all items + virtual void clear() = 0; /* * Signalling diff --git a/src/templates/dataModelTable.h b/src/templates/dataModelTable.h index 0cbc7a9dfe..d62d89e81b 100644 --- a/src/templates/dataModelTable.h +++ b/src/templates/dataModelTable.h @@ -4,6 +4,9 @@ #pragma once #include "templates/dataModelBase.h" +#include +#include +#include namespace DataModel { @@ -18,7 +21,7 @@ template class Table : public Base // Property set function using PropertySetFunction = std::function; - Table(std::vector &data) : Base(), data_(data) + Table() : Base() { // Add properties from the modelable base class for (auto &[name, type, flags, getter, setter] : Modelable::modelableProperties()) @@ -28,11 +31,26 @@ template class Table : public Base } /* - * Target Data and Functions + * Target Data and Vector Compatibility */ private: // Target data for the model - std::vector &data_; + std::vector data_; + + public: + // Return vector + const std::vector &data() const { return data_; } + // Return opening iterator for the data + typename std::vector::const_iterator begin() const { return data_.begin(); } + // Return ending iterator for the data + typename std::vector::const_iterator end() const { return data_.end(); } + // Return whether the data is empty + bool empty() const { return data_.empty(); } + + /* + * Setters / Getters + */ + private: // Map of named properties to data getters std::map getters_; // Map of named properties to data setters @@ -128,44 +146,74 @@ template class Table : public Base * Item Management */ private: - // Item creation function (if required) otherwise default constructor T() will be called - using CreateItemFunction = std::function)>; + // Item creation function (if required) otherwise a suitable default constructor will be called + using CreateItemFunction = std::function; CreateItemFunction createItemFunction_; // Item removal function (if required) using RemoveItemFunction = std::function; RemoveItemFunction removeItemFunction_; + private: + // Create new item + void newItem(std::vector &vector, std::optional position = {}) + { + if (position) + data_.insert(data_.begin() + *position, createItemFunction_ ? createItemFunction_() : DataItem()); + else + data_.emplace_back(createItemFunction_ ? createItemFunction_() : DataItem()); + } + void newItem(std::vector> &vector, std::optional position = {}) + { + if (position) + data_.insert(data_.begin() + *position, + createItemFunction_ ? createItemFunction_() : std::make_shared()); + else + data_.emplace_back(createItemFunction_ ? createItemFunction_() : std::make_shared()); + } + void newItem(std::vector> &vector, std::optional position = {}) + { + if (position) + data_.insert(data_.begin() + *position, + createItemFunction_ ? createItemFunction_() : std::make_unique()); + else + data_.emplace_back(createItemFunction_ ? createItemFunction_() : std::make_unique()); + } + public: // Set data creation function void setCreator(CreateItemFunction function) { createItemFunction_ = std::move(function); } // Set data removal function void setRemover(RemoveItemFunction function) { removeItemFunction_ = std::move(function); } - - public: // Create new item(s) starting at specified vector index void createItems(int index, int count) final { emitMutationSignal(Base::MutationSignal::DataCreationStarted, index, index + count - 1); for (auto n = 0; n < count; ++n) + newItem(data_, index + n); + emitMutationSignal(Base::MutationSignal::DataCreationFinished); + } + // Insert a specified item at back of the vector or a specified position + void insertThis(DataItem &newItem, std::optional position = {}) + { + if (position) { - if (createItemFunction_) - createItemFunction_(index + n); - else - data_.insert(data_.begin() + index, DataItem()); + emitMutationSignal(Base::MutationSignal::DataCreationStarted, *position, *position); + data_.insert(data_.begin() + *position, newItem); + emitMutationSignal(Base::MutationSignal::DataCreationFinished); + } + else + { + emitMutationSignal(Base::MutationSignal::DataCreationStarted, data_.size(), data_.size()); + data_.emplace_back(newItem); + emitMutationSignal(Base::MutationSignal::DataCreationFinished); } - emitMutationSignal(Base::MutationSignal::DataCreationFinished); } // Append new item(s) to the end of the data void appendItems(int count) final { emitMutationSignal(Base::MutationSignal::DataCreationStarted, data_.size(), data_.size() + count - 1); for (auto n = 0; n < count; ++n) - { - if (createItemFunction_) - createItemFunction_({}); - else - data_.emplace_back(DataItem()); - } + newItem(data_); emitMutationSignal(Base::MutationSignal::DataCreationFinished); } // Remove item(s) starting at specified vector index @@ -181,5 +229,21 @@ template class Table : public Base data_.erase(data_.begin() + index, data_.begin() + index + count); emitMutationSignal(Base::MutationSignal::DataRemovalFinished); } + // Clear all items + void clear() final + { + if (empty()) + return; + + emitMutationSignal(Base::MutationSignal::DataRemovalStarted, 0, data_.size() - 1); + if (removeItemFunction_) + { + for (auto n = 0; n < data_.size(); ++n) + removeItemFunction_(0); + } + else + data_.clear(); + emitMutationSignal(Base::MutationSignal::DataRemovalFinished); + } }; } // namespace DataModel From 409528a766941a0ed2c96eea1eb310400e7d0f18 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Sun, 21 Jul 2024 11:57:48 +0100 Subject: [PATCH 27/42] Harmonise creation of ExpressionVariables. --- src/keywords/expressionVariableVector.cpp | 9 +++++++-- src/procedure/nodes/node.cpp | 8 ++++---- src/procedure/nodes/node.h | 3 +-- src/templates/dataModelTable.h | 8 ++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 57097760d6..cb3786527b 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -23,12 +23,17 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword( return true; }); + // Override the creator function since we need to ensure both name uniqueness and the correct prefixing data_.setCreator( [&]() { auto allParameters = parentNode_->getParametersInScope(); - return parentNode_->addParameter( - DissolveSys::uniqueName("NewVariable", allParameters, [](const auto &var) { return var->name(); }), {}); + auto newVar = std::make_shared(); + newVar->setBaseName( + DissolveSys::uniqueName("NewVariable", allParameters, [](const auto &var) { return var->name(); })); + if (parentNode_->type() != ProcedureNode::NodeType::Parameters) + newVar->setNamePrefix(parentNode_->name()); + return newVar; }); } diff --git a/src/procedure/nodes/node.cpp b/src/procedure/nodes/node.cpp index e78c086c53..73b2095847 100644 --- a/src/procedure/nodes/node.cpp +++ b/src/procedure/nodes/node.cpp @@ -154,11 +154,11 @@ OptionalReferenceWrapper ProcedureNode::branch() { return */ // Add new parameter -std::shared_ptr ProcedureNode::addParameter(std::string_view name, const ExpressionValue &initialValue, - std::optional insertAt) +std::shared_ptr ProcedureNode::addParameter(std::string_view name, const ExpressionValue &initialValue) { - auto newVar = std::make_shared(name, initialValue); - parameters_.insertThis(newVar, insertAt); + auto newVar = parameters_.appendItem(); + newVar->setBaseName(name); + newVar->setValue(initialValue); if (type_ != ProcedureNode::NodeType::Parameters) newVar->setNamePrefix(name_); diff --git a/src/procedure/nodes/node.h b/src/procedure/nodes/node.h index 42793df7e8..9f2e6d93a1 100644 --- a/src/procedure/nodes/node.h +++ b/src/procedure/nodes/node.h @@ -140,8 +140,7 @@ class ProcedureNode : public std::enable_shared_from_this, public public: // Add new parameter - std::shared_ptr addParameter(std::string_view name, const ExpressionValue &initialValue = {}, - std::optional insertAt = {}); + std::shared_ptr addParameter(std::string_view name, const ExpressionValue &initialValue = {}); // Return the named parameter (if it exists) std::shared_ptr getParameter(std::string_view name, const std::shared_ptr &excludeParameter = {}); diff --git a/src/templates/dataModelTable.h b/src/templates/dataModelTable.h index d62d89e81b..ae12db6d57 100644 --- a/src/templates/dataModelTable.h +++ b/src/templates/dataModelTable.h @@ -208,6 +208,14 @@ template class Table : public Base emitMutationSignal(Base::MutationSignal::DataCreationFinished); } } + // Append new item to the end of the data and return it + DataItem &appendItem() + { + emitMutationSignal(Base::MutationSignal::DataCreationStarted, data_.size(), data_.size()); + newItem(data_); + emitMutationSignal(Base::MutationSignal::DataCreationFinished); + return data_.back(); + } // Append new item(s) to the end of the data void appendItems(int count) final { From 6d5eff15719db2dcd473cc264d7a19aded943161 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 22 Jul 2024 09:28:11 +0100 Subject: [PATCH 28/42] Rename class. --- src/keywords/expressionVariableVector.cpp | 7 ++++--- src/keywords/expressionVariableVector.h | 12 ++++++------ src/procedure/nodes/node.h | 4 ++-- .../{dataModelTable.h => dataModelVectorModelable.h} | 6 +++--- 4 files changed, 15 insertions(+), 14 deletions(-) rename src/templates/{dataModelTable.h => dataModelVectorModelable.h} (98%) diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index cb3786527b..06b03b8453 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -9,7 +9,7 @@ #include ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword( - DataModel::Table> &data, ProcedureNode *parentNode) + DataModel::VectorModelable> &data, ProcedureNode *parentNode) : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode) { // Override the setter for the "Name" property in the model as we need to ensure unique naming in the same scope @@ -42,11 +42,12 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword( */ // Return reference to data -DataModel::Table> &ExpressionVariableVectorKeyword::data() +DataModel::VectorModelable> &ExpressionVariableVectorKeyword::data() { return data_; } -const DataModel::Table> &ExpressionVariableVectorKeyword::data() const +const DataModel::VectorModelable> & +ExpressionVariableVectorKeyword::data() const { return data_; } diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index b4e56916d0..540c3fbf59 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -5,7 +5,7 @@ #include "expression/node.h" #include "keywords/base.h" -#include "templates/dataModelTable.h" +#include "templates/dataModelVectorModelable.h" // Forward Declarations class ExpressionVariable; @@ -15,7 +15,7 @@ class ProcedureNode; class ExpressionVariableVectorKeyword : public KeywordBase { public: - ExpressionVariableVectorKeyword(DataModel::Table> &data, + ExpressionVariableVectorKeyword(DataModel::VectorModelable> &data, ProcedureNode *parentNode); ~ExpressionVariableVectorKeyword() override = default; @@ -23,8 +23,8 @@ class ExpressionVariableVectorKeyword : public KeywordBase * Data */ private: - // Reference to vector of data - DataModel::Table> &data_; + // Reference to vector modelable + DataModel::VectorModelable> &data_; // Parent ProcedureNode ProcedureNode *parentNode_; @@ -32,8 +32,8 @@ class ExpressionVariableVectorKeyword : public KeywordBase // Has not changed from initial value bool isDefault() const override; // Return reference to vector of data - DataModel::Table> &data(); - const DataModel::Table> &data() const; + DataModel::VectorModelable> &data(); + const DataModel::VectorModelable> &data() const; // Return parent ProcedureNode ProcedureNode *parentNode(); const ProcedureNode *parentNode() const; diff --git a/src/procedure/nodes/node.h b/src/procedure/nodes/node.h index 9f2e6d93a1..582edb0825 100644 --- a/src/procedure/nodes/node.h +++ b/src/procedure/nodes/node.h @@ -9,7 +9,7 @@ #include "keywords/store.h" #include "procedure/nodes/aliases.h" #include "procedure/nodes/context.h" -#include "templates/dataModelTable.h" +#include "templates/dataModelVectorModelable.h" #include "templates/optionalRef.h" // Forward Declarations @@ -133,7 +133,7 @@ class ProcedureNode : public std::enable_shared_from_this, public */ protected: // Defined parameters - DataModel::Table> parameters_; + DataModel::VectorModelable> parameters_; // Set named parameter in supplied vector bool setParameter(std::vector> ¶meters, std::string_view parameter, ExpressionValue value); diff --git a/src/templates/dataModelTable.h b/src/templates/dataModelVectorModelable.h similarity index 98% rename from src/templates/dataModelTable.h rename to src/templates/dataModelVectorModelable.h index ae12db6d57..f14478d9b5 100644 --- a/src/templates/dataModelTable.h +++ b/src/templates/dataModelVectorModelable.h @@ -10,8 +10,8 @@ namespace DataModel { -// Table Model for Data based on std::vector -template class Table : public Base +// Modelable for Data based on std::vector +template class VectorModelable : public Base { public: // Modelable properties @@ -21,7 +21,7 @@ template class Table : public Base // Property set function using PropertySetFunction = std::function; - Table() : Base() + VectorModelable() : Base() { // Add properties from the modelable base class for (auto &[name, type, flags, getter, setter] : Modelable::modelableProperties()) From ed839db1d802857050bb255b212df6583e251a17 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 22 Jul 2024 11:27:37 +0100 Subject: [PATCH 29/42] Constify some accessors. --- src/templates/dataModelBase.h | 6 +++--- src/templates/dataModelVectorModelable.h | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h index c34efe7bb5..629f79e9e9 100644 --- a/src/templates/dataModelBase.h +++ b/src/templates/dataModelBase.h @@ -46,13 +46,13 @@ class Base public: // Return number of data items (i.e. rows) for the specified index - virtual int nDataItems() = 0; + virtual int nDataItems() const = 0; // Return number of properties (i.e. columns) for the specified index - virtual int nProperties() = 0; + virtual int nProperties() const = 0; // Set property function virtual bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) = 0; // Get property function - virtual PropertyValue getProperty(int dataIndex, int propertyIndex) = 0; + virtual PropertyValue getProperty(int dataIndex, int propertyIndex) const = 0; /* * Item Management diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index f14478d9b5..91f18f1d15 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -86,9 +86,9 @@ template class VectorModelable : public Ba public: // Return number of data items (rows) in the table - int nDataItems() final { return data_.size(); } + int nDataItems() const final { return data_.size(); } // Return number of properties per data item (i.e. columns) in the table - int nProperties() final { return itemProperties_.size(); } + int nProperties() const final { return itemProperties_.size(); } /* * Data Access @@ -114,13 +114,13 @@ template class VectorModelable : public Ba public: // Get property - PropertyValue getProperty(int dataIndex, int propertyIndex) final + PropertyValue getProperty(int dataIndex, int propertyIndex) const final { // Check index validity if (!isIndexValid(dataIndex, propertyIndex)) return {}; - return callGetter(getters_[itemProperties_[propertyIndex].name()], data_[dataIndex]); + return callGetter(getters_.at(itemProperties_[propertyIndex].name()), data_[dataIndex]); } // Set property bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) final From e41a4db5c17904ffbe0b94afa86089f061d18873 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 22 Jul 2024 13:39:17 +0100 Subject: [PATCH 30/42] Data mutator and signals. --- src/gui/dataModelTableInterface.cpp | 6 ++++++ src/templates/dataModelBase.h | 3 ++- src/templates/dataModelVectorModelable.h | 21 ++++++++++++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/gui/dataModelTableInterface.cpp b/src/gui/dataModelTableInterface.cpp index dd176467f5..4570b4b9ff 100644 --- a/src/gui/dataModelTableInterface.cpp +++ b/src/gui/dataModelTableInterface.cpp @@ -128,6 +128,12 @@ void DataModelTableInterface::dataMutated(DataModel::Base::MutationSignal signal { switch (signal) { + case (DataModel::Base::MutationSignal::DataMutationStarted): + beginResetModel(); + break; + case (DataModel::Base::MutationSignal::DataMutationFinished): + endResetModel(); + break; case (DataModel::Base::MutationSignal::DataCreationStarted): beginInsertRows({}, startIndex, endIndex); break; diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h index 629f79e9e9..3d7beb79e7 100644 --- a/src/templates/dataModelBase.h +++ b/src/templates/dataModelBase.h @@ -74,7 +74,8 @@ class Base // Mutation Signals enum class MutationSignal { - PropertyChanged, + DataMutationStarted, + DataMutationFinished, DataCreationStarted, DataCreationFinished, DataRemovalStarted, diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index 91f18f1d15..31f89a3cd1 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -10,6 +10,20 @@ namespace DataModel { +// Mutator for Data based on std::vector +template class VectorMutator +{ + public: + VectorMutator(std::vector &data, Base &base) : data_(data), base_(base) + { + base_.emitMutationSignal(Base::MutationSignal::DataMutationStarted); + } + ~VectorMutator() { base_.emitMutationSignal(Base::MutationSignal::DataMutationFinished); } + + private: + std::vector &data_; + Base &base_; +}; // Modelable for Data based on std::vector template class VectorModelable : public Base { @@ -38,8 +52,13 @@ template class VectorModelable : public Ba std::vector data_; public: - // Return vector + // Return const data const std::vector &data() const { return data_; } + // Return mutable data + std::pair, std::vector &> mutableData() + { + return {VectorModelable(data_, &this), data_}; + }; // Return opening iterator for the data typename std::vector::const_iterator begin() const { return data_.begin(); } // Return ending iterator for the data From 38de575dc69da7d871ca729f83a0859a56bd0b30 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 22 Jul 2024 13:39:32 +0100 Subject: [PATCH 31/42] More const. --- src/templates/dataModelVectorModelable.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index 31f89a3cd1..5e7ef03e9f 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -114,9 +114,15 @@ template class VectorModelable : public Ba */ private: // Call getter - PropertyValue callGetter(PropertyGetFunction &getter, DataItemClass &item) { return getter(&item); } - PropertyValue callGetter(PropertyGetFunction &getter, std::shared_ptr &item) { return getter(item.get()); } - PropertyValue callGetter(PropertyGetFunction &getter, std::unique_ptr &item) { return getter(item.get()); } + PropertyValue callGetter(const PropertyGetFunction &getter, const DataItemClass &item) const { return getter(&item); } + PropertyValue callGetter(const PropertyGetFunction &getter, const std::shared_ptr &item) const + { + return getter(item.get()); + } + PropertyValue callGetter(const PropertyGetFunction &getter, const std::unique_ptr &item) const + { + return getter(item.get()); + } // Call setter bool callSetter(PropertySetFunction &setter, DataItemClass &item, const PropertyValue &newValue) { From 9060e295683b418944880b8c7bbe58d818d45a0a Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 22 Jul 2024 13:39:41 +0100 Subject: [PATCH 32/42] Add emplaceAppend function. --- src/templates/dataModelVectorModelable.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index 5e7ef03e9f..78a3967e9e 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -249,6 +249,13 @@ template class VectorModelable : public Ba newItem(data_); emitMutationSignal(Base::MutationSignal::DataCreationFinished); } + // Emplace append the supplied item + void emplaceAppend(DataItem item) + { + emitMutationSignal(Base::MutationSignal::DataCreationStarted, data_.size(), data_.size()); + data_.emplace_back(std::move(item)); + emitMutationSignal(Base::MutationSignal::DataCreationFinished); + } // Remove item(s) starting at specified vector index void removeItems(int index, int count) final { From 8cc9feedde874bcc2169c853334b05547c7601a8 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 22 Jul 2024 14:01:29 +0100 Subject: [PATCH 33/42] Don't assume the use std::make_shared or std::make_unique since we might be storing base (pure virtual) classes. --- src/keywords/expressionVariableVector.cpp | 2 +- src/templates/dataModelVectorModelable.h | 49 +++++------------------ 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 06b03b8453..fd0589389a 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -23,7 +23,7 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword( return true; }); - // Override the creator function since we need to ensure both name uniqueness and the correct prefixing + // Set the creator function - we need to ensure both name uniqueness and the correct prefixing data_.setCreator( [&]() { diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index 78a3967e9e..06cae182e9 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -180,28 +180,17 @@ template class VectorModelable : public Ba private: // Create new item - void newItem(std::vector &vector, std::optional position = {}) - { - if (position) - data_.insert(data_.begin() + *position, createItemFunction_ ? createItemFunction_() : DataItem()); - else - data_.emplace_back(createItemFunction_ ? createItemFunction_() : DataItem()); - } - void newItem(std::vector> &vector, std::optional position = {}) - { - if (position) - data_.insert(data_.begin() + *position, - createItemFunction_ ? createItemFunction_() : std::make_shared()); - else - data_.emplace_back(createItemFunction_ ? createItemFunction_() : std::make_shared()); - } - void newItem(std::vector> &vector, std::optional position = {}) + void newItem(std::optional position = {}) { + if (!createItemFunction_) + throw(std::runtime_error( + fmt::format("No CreateItemFunction has been set in this VectorModelable({},{}) but newItem() was called.\n", + typeid(DataItemClass).name(), typeid(DataItem).name()))); + if (position) - data_.insert(data_.begin() + *position, - createItemFunction_ ? createItemFunction_() : std::make_unique()); + data_.insert(data_.begin() + *position, createItemFunction_()); else - data_.emplace_back(createItemFunction_ ? createItemFunction_() : std::make_unique()); + data_.emplace_back(createItemFunction_()); } public: @@ -214,30 +203,14 @@ template class VectorModelable : public Ba { emitMutationSignal(Base::MutationSignal::DataCreationStarted, index, index + count - 1); for (auto n = 0; n < count; ++n) - newItem(data_, index + n); + newItem(index + n); emitMutationSignal(Base::MutationSignal::DataCreationFinished); } - // Insert a specified item at back of the vector or a specified position - void insertThis(DataItem &newItem, std::optional position = {}) - { - if (position) - { - emitMutationSignal(Base::MutationSignal::DataCreationStarted, *position, *position); - data_.insert(data_.begin() + *position, newItem); - emitMutationSignal(Base::MutationSignal::DataCreationFinished); - } - else - { - emitMutationSignal(Base::MutationSignal::DataCreationStarted, data_.size(), data_.size()); - data_.emplace_back(newItem); - emitMutationSignal(Base::MutationSignal::DataCreationFinished); - } - } // Append new item to the end of the data and return it DataItem &appendItem() { emitMutationSignal(Base::MutationSignal::DataCreationStarted, data_.size(), data_.size()); - newItem(data_); + newItem(); emitMutationSignal(Base::MutationSignal::DataCreationFinished); return data_.back(); } @@ -246,7 +219,7 @@ template class VectorModelable : public Ba { emitMutationSignal(Base::MutationSignal::DataCreationStarted, data_.size(), data_.size() + count - 1); for (auto n = 0; n < count; ++n) - newItem(data_); + newItem(); emitMutationSignal(Base::MutationSignal::DataCreationFinished); } // Emplace append the supplied item From 02ef71496a0f6108a296fbefa3c0b49c472745db Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 22 Jul 2024 14:20:17 +0100 Subject: [PATCH 34/42] Fix mutator return function. --- src/templates/dataModelVectorModelable.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index 06cae182e9..fe2f3d9089 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -55,10 +55,7 @@ template class VectorModelable : public Ba // Return const data const std::vector &data() const { return data_; } // Return mutable data - std::pair, std::vector &> mutableData() - { - return {VectorModelable(data_, &this), data_}; - }; + std::pair, std::vector &> mutableData() { return {{data_, *this}, data_}; }; // Return opening iterator for the data typename std::vector::const_iterator begin() const { return data_.begin(); } // Return ending iterator for the data From 9aefc63f431d57b07ad98f21ee38c832ddcfc4bd Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Thu, 25 Jul 2024 10:51:50 +0100 Subject: [PATCH 35/42] Apply suggestions from code review Co-authored-by: Adam Washington --- src/gui/dataModelTableInterface.cpp | 12 +++++++++--- src/templates/dataModelItem.h | 9 +-------- src/templates/dataModelVectorModelable.h | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/gui/dataModelTableInterface.cpp b/src/gui/dataModelTableInterface.cpp index 4570b4b9ff..6dbb62c5ae 100644 --- a/src/gui/dataModelTableInterface.cpp +++ b/src/gui/dataModelTableInterface.cpp @@ -52,9 +52,15 @@ QVariant DataModelTableInterface::data(const QModelIndex &index, int role) const // Construct a QVariant from the contents of our std::variant return std::visit( - DataModel::PropertyVisitor{[](int arg) { return QVariant(arg); }, [](double arg) { return QVariant(arg); }, - [](std::string_view arg) { return QVariant(QString::fromStdString(std::string(arg))); }, - [](std::string &arg) { return QVariant(QString::fromStdString(arg)); }}, + [](auto arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) + return QVariant(QString::fromStdString(std::string(arg))); + else if constexpr (std::is_same_v) + return QVariant(QString::fromStdString(arg)); + else + return QVariant(arg); + }, property); } diff --git a/src/templates/dataModelItem.h b/src/templates/dataModelItem.h index 8cb217f09b..e0f84ca00f 100644 --- a/src/templates/dataModelItem.h +++ b/src/templates/dataModelItem.h @@ -55,14 +55,7 @@ using PropertyValue = std::variant; // Return value as string static std::string propertyAsString(const PropertyValue &value) { - if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else if (std::holds_alternative(value)) - return fmt::format("{}", std::get(value)); - else - return std::get(value); + return std::visit([](auto &arg) {return fmt::format("{}", arg);}, value); } // Helper type for PropertyValue visitor diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index fe2f3d9089..628513199f 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -153,7 +153,7 @@ template class VectorModelable : public Ba if (itemProperties_[propertyIndex].flags().isSet(ItemProperty::PropertyFlag::ReadOnly)) { - fmt::print("Refusing to set data '{}' since it is read-only.\n", itemProperties_[propertyIndex].name()); + Messenger::error("Refusing to set data '{}' since it is read-only.\n", itemProperties_[propertyIndex].name()); return false; } From b3dc1cc654d3a24d988152e018ff05ae8b1863fe Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Thu, 25 Jul 2024 10:57:54 +0100 Subject: [PATCH 36/42] Formatting, remove PropertyVisitor voodoo. --- src/gui/dataModelTableInterface.cpp | 7 ++++--- src/templates/dataModelItem.h | 10 +--------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/gui/dataModelTableInterface.cpp b/src/gui/dataModelTableInterface.cpp index 6dbb62c5ae..8a61d383c3 100644 --- a/src/gui/dataModelTableInterface.cpp +++ b/src/gui/dataModelTableInterface.cpp @@ -52,12 +52,13 @@ QVariant DataModelTableInterface::data(const QModelIndex &index, int role) const // Construct a QVariant from the contents of our std::variant return std::visit( - [](auto arg) { + [](auto arg) + { using T = std::decay_t; if constexpr (std::is_same_v) - return QVariant(QString::fromStdString(std::string(arg))); + return QVariant(QString::fromStdString(std::string(arg))); else if constexpr (std::is_same_v) - return QVariant(QString::fromStdString(arg)); + return QVariant(QString::fromStdString(arg)); else return QVariant(arg); }, diff --git a/src/templates/dataModelItem.h b/src/templates/dataModelItem.h index e0f84ca00f..cfe074caf5 100644 --- a/src/templates/dataModelItem.h +++ b/src/templates/dataModelItem.h @@ -55,17 +55,9 @@ using PropertyValue = std::variant; // Return value as string static std::string propertyAsString(const PropertyValue &value) { - return std::visit([](auto &arg) {return fmt::format("{}", arg);}, value); + return std::visit([](auto &arg) { return fmt::format("{}", arg); }, value); } -// Helper type for PropertyValue visitor -template struct PropertyVisitor : Ts... -{ - using Ts::operator()...; -}; -// Explicit deduction guide for PropertyValue visitor -template PropertyVisitor(Ts...) -> PropertyVisitor; - // Modelable - Provides declarations for use by a class defining its properties template class Modelable { From 6da3e428f98cca9ef3b4c37b0ad11338c5478ca4 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Thu, 25 Jul 2024 11:07:11 +0100 Subject: [PATCH 37/42] Update src/templates/dataModelItem.h Co-authored-by: Adam Washington --- src/templates/dataModelItem.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/templates/dataModelItem.h b/src/templates/dataModelItem.h index cfe074caf5..3253babba5 100644 --- a/src/templates/dataModelItem.h +++ b/src/templates/dataModelItem.h @@ -64,8 +64,7 @@ template class Modelable public: using ModelableGetter = std::function; using ModelableSetter = std::function; - using ModelableProperty = std::tuple, - ModelableGetter, ModelableSetter>; + using ModelableProperty = std::tuple; /* * We now define a static templated function to return a vector of ModelableProperty definitions for the target class D. The From 2491ca56a851e4c49f774202175121a227cf56e2 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Thu, 25 Jul 2024 16:08:44 +0100 Subject: [PATCH 38/42] Refactor use of ItemProperty after previous change. --- src/expression/variable.cpp | 12 +++--------- src/templates/dataModelBase.h | 6 +----- src/templates/dataModelVectorModelable.h | 13 ++++++------- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/expression/variable.cpp b/src/expression/variable.cpp index ba7f7259e0..96be169261 100644 --- a/src/expression/variable.cpp +++ b/src/expression/variable.cpp @@ -82,26 +82,20 @@ template <> const std::vector::ModelableProperty> DataModel::Modelable::modelableProperties() { - return {{"Name", - DataModel::ItemProperty::PropertyType::String, - {}, + return {{{"Name", DataModel::ItemProperty::PropertyType::String, {}}, [&](const ExpressionVariable *var) { return DataModel::PropertyValue(var->baseName()); }, [&](ExpressionVariable *var, const DataModel::PropertyValue &newValue) { var->setBaseName(DataModel::propertyAsString(newValue)); return true; }}, - {"Type", - DataModel::ItemProperty::PropertyType::String, - {DataModel::ItemProperty::PropertyFlag::ReadOnly}, + {{"Type", DataModel::ItemProperty::PropertyType::String, {DataModel::ItemProperty::PropertyFlag::ReadOnly}}, [&](const ExpressionVariable *var) { return DataModel::PropertyValue( std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); }, [&](ExpressionVariable *var, const DataModel::PropertyValue &newValue) { return false; }}, - {"Value", - DataModel::ItemProperty::PropertyType::String, - {}, + {{"Value", DataModel::ItemProperty::PropertyType::String, {}}, [&](const ExpressionVariable *var) { return DataModel::PropertyValue(var->value().asString()); }, [&](ExpressionVariable *var, const DataModel::PropertyValue &newValue) { diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h index 3d7beb79e7..ec4a32daf2 100644 --- a/src/templates/dataModelBase.h +++ b/src/templates/dataModelBase.h @@ -25,11 +25,7 @@ class Base protected: // Add item property - void addItemProperty(std::string_view name, ItemProperty::PropertyType type, - const Flags &flags = {}) - { - itemProperties_.emplace_back(name, type, flags); - } + void addItemProperty(const ItemProperty &property) { itemProperties_.emplace_back(property); } public: // Return name of specified property diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index 628513199f..95f74686ef 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -38,9 +38,9 @@ template class VectorModelable : public Ba VectorModelable() : Base() { // Add properties from the modelable base class - for (auto &[name, type, flags, getter, setter] : Modelable::modelableProperties()) + for (auto &[itemProperty, getter, setter] : Modelable::modelableProperties()) { - addProperty(name, type, flags, getter, setter); + addProperty(itemProperty, getter, setter); } } @@ -74,16 +74,15 @@ template class VectorModelable : public Ba private: // Add item property for use in the model - void addProperty(const std::string &name, ItemProperty::PropertyType type, Flags flags, - PropertyGetFunction getter, PropertySetFunction setter = {}) + void addProperty(const ItemProperty &property, PropertyGetFunction getter, PropertySetFunction setter = {}) { // Add the property base info - the order will be reflected in the table model - addItemProperty(name, type, flags); + addItemProperty(property); // Store functions - getters_[name] = std::move(getter); + getters_[property.name()] = std::move(getter); if (setter) - setters_[name] = std::move(setter); + setters_[property.name()] = std::move(setter); } public: From eab2c665db4b52dd4e1ea4c9f9d193406f882572 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Thu, 25 Jul 2024 16:14:37 +0100 Subject: [PATCH 39/42] No need to override nProperties(). --- src/templates/dataModelBase.h | 2 +- src/templates/dataModelVectorModelable.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h index ec4a32daf2..e122062bc9 100644 --- a/src/templates/dataModelBase.h +++ b/src/templates/dataModelBase.h @@ -44,7 +44,7 @@ class Base // Return number of data items (i.e. rows) for the specified index virtual int nDataItems() const = 0; // Return number of properties (i.e. columns) for the specified index - virtual int nProperties() const = 0; + int nProperties() const { return itemProperties_.size(); } // Set property function virtual bool setProperty(int dataIndex, int propertyIndex, const PropertyValue &newValue) = 0; // Get property function diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index 95f74686ef..b0cf0f396e 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -102,8 +102,6 @@ template class VectorModelable : public Ba public: // Return number of data items (rows) in the table int nDataItems() const final { return data_.size(); } - // Return number of properties per data item (i.e. columns) in the table - int nProperties() const final { return itemProperties_.size(); } /* * Data Access From 97cc62e2647ea035c8d6b16af494fc2966448810 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Thu, 25 Jul 2024 16:25:15 +0100 Subject: [PATCH 40/42] Public access to data vector of VectorMutator, and don't bother with returning a pair.. --- src/templates/dataModelVectorModelable.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index b0cf0f396e..7c4cbf4803 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -21,8 +21,13 @@ template class VectorMutator ~VectorMutator() { base_.emitMutationSignal(Base::MutationSignal::DataMutationFinished); } private: + // Data being mutated and the base model std::vector &data_; Base &base_; + + public: + // Return mutable data + std::vector &data() { return data_; } }; // Modelable for Data based on std::vector template class VectorModelable : public Base @@ -55,7 +60,7 @@ template class VectorModelable : public Ba // Return const data const std::vector &data() const { return data_; } // Return mutable data - std::pair, std::vector &> mutableData() { return {{data_, *this}, data_}; }; + VectorMutator mutableData() { return {data_, *this}; }; // Return opening iterator for the data typename std::vector::const_iterator begin() const { return data_.begin(); } // Return ending iterator for the data From a6ba1a3b620c4454ade8087e0b0ccbfdb5d15cee Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 29 Jul 2024 09:54:17 +0100 Subject: [PATCH 41/42] Add lambda-matched removeItem() function. --- src/templates/dataModelVectorModelable.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/templates/dataModelVectorModelable.h b/src/templates/dataModelVectorModelable.h index 7c4cbf4803..0f8a45e589 100644 --- a/src/templates/dataModelVectorModelable.h +++ b/src/templates/dataModelVectorModelable.h @@ -228,6 +228,17 @@ template class VectorModelable : public Ba data_.emplace_back(std::move(item)); emitMutationSignal(Base::MutationSignal::DataCreationFinished); } + // Remove item using the supplied lamba match function + void removeItem(std::function matchFunction) + { + auto it = std::find_if(data_.begin(), data_.end(), matchFunction); + if (it == data_.end()) + return; + + emitMutationSignal(Base::MutationSignal::DataRemovalStarted, it - data_.begin(), it - data_.begin()); + data_.erase(it); + emitMutationSignal(Base::MutationSignal::DataRemovalFinished); + } // Remove item(s) starting at specified vector index void removeItems(int index, int count) final { From ef02e84f1525831d3ce1ba4fe4658bad6026d67e Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 29 Jul 2024 10:10:54 +0100 Subject: [PATCH 42/42] Register signal fuctions per-object. --- src/gui/dataModelTableInterface.cpp | 9 ++++++++- src/gui/dataModelTableInterface.h | 1 + src/templates/dataModelBase.h | 20 ++++++++++++++------ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/gui/dataModelTableInterface.cpp b/src/gui/dataModelTableInterface.cpp index 8a61d383c3..b2a15086da 100644 --- a/src/gui/dataModelTableInterface.cpp +++ b/src/gui/dataModelTableInterface.cpp @@ -6,10 +6,17 @@ DataModelTableInterface::DataModelTableInterface(DataModel::Base &dataModel) : dataModel_(dataModel) { - dataModel_.setMutationSignalFunction([this](DataModel::Base::MutationSignal signal, int startIndex, int endIndex) + // Receive signals from the underyling model + dataModel_.addMutationSignalFunction(this, [this](DataModel::Base::MutationSignal signal, int startIndex, int endIndex) { dataMutated(signal, startIndex, endIndex); }); } +DataModelTableInterface::~DataModelTableInterface() +{ + // Remove our signal from the underlying model + dataModel_.removeMutationSignalFunction(this); +} + /* * QAbstractTableModel Overrides */ diff --git a/src/gui/dataModelTableInterface.h b/src/gui/dataModelTableInterface.h index f33c477215..e3b94bdf16 100644 --- a/src/gui/dataModelTableInterface.h +++ b/src/gui/dataModelTableInterface.h @@ -12,6 +12,7 @@ class DataModelTableInterface : public QAbstractTableModel { public: DataModelTableInterface(DataModel::Base &dataModel); + ~DataModelTableInterface(); private: // Model with which to interface diff --git a/src/templates/dataModelBase.h b/src/templates/dataModelBase.h index e122062bc9..3612abea33 100644 --- a/src/templates/dataModelBase.h +++ b/src/templates/dataModelBase.h @@ -4,6 +4,7 @@ #pragma once #include "templates/dataModelItem.h" +#include namespace DataModel { @@ -81,19 +82,26 @@ class Base private: // Mutation signal function used to notify an associated Qt model using DataMutationSignalFunction = std::function; - DataMutationSignalFunction mutationSignalFunction_ = {}; + std::map mutationSignalFunctions_; public: - // Set mutation signal function - void setMutationSignalFunction(DataMutationSignalFunction mutationSignalFunction) + // Add mutation signal function + void addMutationSignalFunction(void *object, const DataMutationSignalFunction &mutationSignalFunction) { - mutationSignalFunction_ = std::move(mutationSignalFunction); + mutationSignalFunctions_[object] = mutationSignalFunction; + } + // Remove mutation signal function for specified object + void removeMutationSignalFunction(void *object) + { + auto it = mutationSignalFunctions_.find(object); + if (it != mutationSignalFunctions_.end()) + mutationSignalFunctions_.erase(it); } // Emit mutation signal void emitMutationSignal(MutationSignal signal, int startIndex = 0, int endIndex = 0) { - if (mutationSignalFunction_) - mutationSignalFunction_(signal, startIndex, endIndex); + for (const auto &[object, func] : mutationSignalFunctions_) + func(signal, startIndex, endIndex); } }; } // namespace DataModel