From 84f684c3aa589676ccde8b474493fb01ee6ee849 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Sun, 2 Jun 2024 00:05:04 +0100 Subject: [PATCH] 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;