Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Common data table model #1959

Draft
wants to merge 42 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a300304
Starting to implement add/remove buttons for variable removal.
trisyoungs May 31, 2024
ac9cb7e
Start sketching out C++-side custom model.
trisyoungs Jun 1, 2024
84f684c
All working so far.
trisyoungs Jun 1, 2024
540b341
Move to using std::variant.
trisyoungs Jul 9, 2024
85c271f
Simplify table extent determination.
trisyoungs Jul 9, 2024
e17edf4
Error message.
trisyoungs Jul 9, 2024
df2310f
Move to new files.
trisyoungs Jul 9, 2024
7e8e7de
Comments.
trisyoungs Jul 10, 2024
e590227
Rename.
trisyoungs Jul 10, 2024
62311f6
Remove testing code from ExpressionVariableVectorKeyword.
trisyoungs Jul 10, 2024
32d9d80
Begin implementing data mutation links.
trisyoungs Jul 10, 2024
f8541f6
Tidy up.
trisyoungs Jul 16, 2024
5701c52
Insert and append items.
trisyoungs Jul 16, 2024
0e785f0
Fix, tidy.
trisyoungs Jul 16, 2024
bfb5b7e
Fix logic, update selection behaviour.
trisyoungs Jul 16, 2024
ded7b15
Remove rows.
trisyoungs Jul 16, 2024
026cd70
User per-property setters and getters.
trisyoungs Jul 17, 2024
778b0e5
Return flags.
trisyoungs Jul 17, 2024
df41e52
Namespace and split up.
trisyoungs Jul 19, 2024
c2cd9ca
Give both base and storage classes to model to try and handle differe…
trisyoungs Jul 19, 2024
5f4a2d4
Allow setters to be overriden for specific purposes.
trisyoungs Jul 19, 2024
dc7c7d7
Rename functions for consistency.
trisyoungs Jul 19, 2024
83ea61a
Use a static templated function specialisation to provide the propert…
trisyoungs Jul 19, 2024
e3b82cf
Rename Qt interface for consistency.
trisyoungs Jul 20, 2024
a6b7222
Tidy up base model.
trisyoungs Jul 20, 2024
75cbe14
Sneaky overloading of an item creation function to handle bare vs poi…
trisyoungs Jul 21, 2024
409528a
Harmonise creation of ExpressionVariables.
trisyoungs Jul 21, 2024
6d5eff1
Rename class.
trisyoungs Jul 22, 2024
ed839db
Constify some accessors.
trisyoungs Jul 22, 2024
e41a4db
Data mutator and signals.
trisyoungs Jul 22, 2024
38de575
More const.
trisyoungs Jul 22, 2024
9060e29
Add emplaceAppend function.
trisyoungs Jul 22, 2024
8cc9fee
Don't assume the use std::make_shared or std::make_unique since we mi…
trisyoungs Jul 22, 2024
02ef714
Fix mutator return function.
trisyoungs Jul 22, 2024
9aefc63
Apply suggestions from code review
trisyoungs Jul 25, 2024
b3dc1cc
Formatting, remove PropertyVisitor voodoo.
trisyoungs Jul 25, 2024
6da3e42
Update src/templates/dataModelItem.h
trisyoungs Jul 25, 2024
2491ca5
Refactor use of ItemProperty after previous change.
trisyoungs Jul 25, 2024
eab2c66
No need to override nProperties().
trisyoungs Jul 25, 2024
97cc62e
Public access to data vector of VectorMutator, and don't bother with …
trisyoungs Jul 25, 2024
a6ba1a3
Add lambda-matched removeItem() function.
trisyoungs Jul 29, 2024
ef02e84
Register signal fuctions per-object.
trisyoungs Jul 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/expression/variable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "expression/variable.h"
#include "base/messenger.h"
#include "base/sysFunc.h"
#include <cstring>

ExpressionVariable::ExpressionVariable(const ExpressionValue &value)
Expand Down Expand Up @@ -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_}}; }

Expand All @@ -67,3 +72,41 @@ void ExpressionVariable::deserialise(const SerialisedValue &node)
value_ = toml::find<ExpressionValue>(node, "value");
setBaseName(toml::find<std::string>(node, "name"));
}

/*
* Modelable
*/

// Return property getters and basic setters (if relevant)
template <>
const std::vector<DataModel::Modelable<ExpressionVariable>::ModelableProperty>
DataModel::Modelable<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;
}}};
}
8 changes: 7 additions & 1 deletion src/expression/variable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExpressionVariable>
{
public:
ExpressionVariable(const ExpressionValue &value = ExpressionValue());
Expand Down Expand Up @@ -45,6 +46,11 @@ 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
Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ add_library(
configurationViewerWidget.cpp
configurationViewerWidget.h
configurationViewerWidget.ui
dataModelTableInterface.cpp
dataModelTableInterface.h
dataViewer.cpp
dataViewer_input.cpp
dataViewer_interaction.cpp
Expand Down
167 changes: 167 additions & 0 deletions src/gui/dataModelTableInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#include "gui/dataModelTableInterface.h"
#include "procedure/nodes/node.h"

DataModelTableInterface::DataModelTableInterface(DataModel::Base &dataModel) : dataModel_(dataModel)
{
// 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
*/

// Return row count
int DataModelTableInterface::rowCount(const QModelIndex &parent) const { return dataModel_.nDataItems(); }

// Return column count
int DataModelTableInterface::columnCount(const QModelIndex &parent) const { return dataModel_.nProperties(); }

// Return flags for the specified model index
Qt::ItemFlags DataModelTableInterface::flags(const QModelIndex &index) const
{
auto &propertyFlags = dataModel_.propertyFlags(index.column());
Qt::ItemFlags flags = Qt::ItemIsSelectable;
if (!propertyFlags.isSet(DataModel::ItemProperty::ReadOnly))
flags.setFlag(Qt::ItemIsEditable);
if (!propertyFlags.isSet(DataModel::ItemProperty::Disabled))
flags.setFlag(Qt::ItemIsEnabled);
return flags;
}

// Return header data for the specified section, orientation, and role
QVariant DataModelTableInterface::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
return {};

return QString::fromStdString(dataModel_.propertyName(section));
}

// Return data for the index and role specified
QVariant DataModelTableInterface::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole && role != Qt::EditRole)
return {};

// Get the specified data property
auto property = dataModel_.getProperty(index.row(), index.column());

// Construct a QVariant from the contents of our std::variant
return std::visit(
[](auto arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, std::string_view>)
return QVariant(QString::fromStdString(std::string(arg)));
else if constexpr (std::is_same_v<T, std::string>)
return QVariant(QString::fromStdString(arg));
else
return QVariant(arg);
},
property);
}

// Set data for the index and role specified
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;

// Set new value
bool success = false;
switch (dataModel_.propertyType(index.column()))
{
case (DataModel::ItemProperty::PropertyType::Integer):
success = dataModel_.setProperty(index.row(), index.column(), DataModel::PropertyValue(value.toInt()));
break;
case (DataModel::ItemProperty::PropertyType::Double):
success = dataModel_.setProperty(index.row(), index.column(), DataModel::PropertyValue(value.toDouble()));
break;
case (DataModel::ItemProperty::PropertyType::String):
success =
dataModel_.setProperty(index.row(), index.column(), DataModel::PropertyValue(value.toString().toStdString()));
break;
default:
Messenger::error("DataModelTableInterface doesn't know how to handle this PropertyType.\n");
break;
}

if (success)
Q_EMIT dataChanged(index, index);

return success;
}

// Insert one or more rows at the specified position
bool DataModelTableInterface::insertRows(int row, int count, const QModelIndex &parent)
{
Q_UNUSED(parent);

dataModel_.createItems(row, count);

return true;
}

// Append one or more rows
bool DataModelTableInterface::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 DataModelTableInterface::removeRows(int row, int count, const QModelIndex &parent)
{
Q_UNUSED(parent);

dataModel_.removeItems(row, count);

return true;
}

/*
* Mutation Interface
*/

// React to a mutation in the model
void DataModelTableInterface::dataMutated(DataModel::Base::MutationSignal signal, int startIndex, int endIndex)
{
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;
case (DataModel::Base::MutationSignal::DataCreationFinished):
endInsertRows();
break;
case (DataModel::Base::MutationSignal::DataRemovalStarted):
beginRemoveRows({}, startIndex, endIndex);
break;
case (DataModel::Base::MutationSignal::DataRemovalFinished):
endRemoveRows();
break;
default:
Messenger::error("DataModelTableInterface::dataMutated() was passed a signal it didn't recognise...\n");
break;
}
}
50 changes: 50 additions & 0 deletions src/gui/dataModelTableInterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#pragma once

#include "templates/dataModelBase.h"
#include <QAbstractTableModel>
#include <QModelIndex>

// QAbstractTableModel Interface to DataModel::Table
class DataModelTableInterface : public QAbstractTableModel
{
public:
DataModelTableInterface(DataModel::Base &dataModel);
~DataModelTableInterface();

private:
// Model with which to interface
DataModel::Base &dataModel_;

/*
* QAbstractTableModel Overrides
*/
public:
// 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;
// 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;

/*
* Mutation Interface
*/
private:
// React to a mutation in the model
void dataMutated(DataModel::Base::MutationSignal signal, int startIndex, int endIndex);
};
24 changes: 19 additions & 5 deletions src/gui/keywordWidgets/expressionVariableVector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@
ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWidget *parent,
ExpressionVariableVectorKeyword *keyword,
const CoreData &coreData)
: QWidget(parent), KeywordWidgetBase(coreData), keyword_(keyword)
: QWidget(parent), KeywordWidgetBase(coreData), keyword_(keyword), variableModel_(keyword->data())
{
// 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
connect(&variableModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &)), 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));
Expand All @@ -31,13 +32,26 @@ 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;

Q_EMIT(keywordDataChanged(keyword_->editSignals()));
}

void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItemSelection &current,
const QItemSelection &previous)
{
ui_.RemoveVariableButton->setEnabled(!current.empty());
}

void ExpressionVariableVectorKeywordWidget::on_AddVariableButton_clicked(bool checked) { variableModel_.appendRows(1, {}); }

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<DissolveSignals::DataMutations> &mutationFlags) {}
12 changes: 6 additions & 6 deletions src/gui/keywordWidgets/expressionVariableVector.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@

#pragma once

#include "gui/dataModelTableInterface.h"
#include "gui/keywordWidgets/base.h"
#include "gui/keywordWidgets/ui_expressionVariableVector.h"
#include "gui/models/expressionVariableVectorModel.h"
#include "keywords/expressionVariableVector.h"

// Forward Declarations
class QWidget;

class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidgetBase
{
// All Qt declarations must include this macro
Expand All @@ -33,10 +30,13 @@ class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidg
// Main form declaration
Ui::ExpressionVariableVectorWidget ui_;
// Model for table
ExpressionVariableVectorModel variableModel_;
DataModelTableInterface variableModel_;

private Q_SLOTS:
void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void variableSelectionChanged(const QItemSelection &current, const QItemSelection &previous);
void on_AddVariableButton_clicked(bool checked);
void on_RemoveVariableButton_clicked(bool checked);

Q_SIGNALS:
// Keyword data changed
Expand Down
Loading
Loading