Skip to content

Commit

Permalink
Add property model. Still WIP
Browse files Browse the repository at this point in the history
Signed-off-by: Geoff Hutchison <[email protected]>
  • Loading branch information
ghutchis committed Oct 28, 2024
1 parent b3681cf commit a90aa27
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 222 deletions.
204 changes: 61 additions & 143 deletions avogadro/qtplugins/molecularproperties/molecularmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <avogadro/qtgui/molecule.h>

#include <QtCore/QDebug>
#include <QtCore/QRegularExpression>
#include <QtGui/QColor>

#include <limits>
Expand All @@ -18,17 +19,17 @@ namespace Avogadro {

using Avogadro::QtGui::Molecule;
using QtGui::Molecule;
using QtGui::RWAtom;
using QtGui::RWBond;
using std::numeric_limits;
using std::pair;
using std::vector;

MolecularModel::MolecularModel(QObject* parent)
: QAbstractTableModel(parent), m_molecule(nullptr)
{
}

void MolecularModel::setMolecule(QtGui::Molecule* molecule)
{
m_molecule = molecule;
}

int MolecularModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
Expand All @@ -42,13 +43,12 @@ int MolecularModel::rowCount(const QModelIndex& parent) const
// and then however many keys are in the property map
int rows = 5;
if (m_molecule->residueCount() > 0)
rows += 2;
rows += 1; // TODO chains
if (m_molecule->coordinate3dCount() > 0)
++rows;

const auto& properties = m_molecule->dataMap();
if (m_molecule->propertyKeys().size() > 0)
rows += m_molecule->propertyKeys().size();
rows += properties.names().size(); // 0 or more

return 0;
}
Expand Down Expand Up @@ -94,7 +94,7 @@ QVariant MolecularModel::data(const QModelIndex& index, int role) const

// handle text alignments
if (role == Qt::TextAlignmentRole) {
return toVariant(Qt::AlignHCenter | Qt::AlignVRight);
return toVariant(Qt::AlignHCenter | Qt::AlignRight);
}

if (role != Qt::UserRole && role != Qt::DisplayRole && role != Qt::EditRole)
Expand All @@ -107,26 +107,35 @@ QVariant MolecularModel::data(const QModelIndex& index, int role) const
} else if (row == Formula) {
return formatFormula(m_molecule->formula());
} else if (row == Atoms) {
return m_molecule->atomCount();
return QVariant::fromValue(m_molecule->atomCount());
} else if (row == Bonds) {
return m_molecule->bondCount();
return QVariant::fromValue(m_molecule->bondCount());
}

// TODO: figure out if we have conformers, etc.

/*
} else if (row == Residues) {
return m_molecule->residueCount();
} else if (row == Chains) {
return m_molecule->chainCount();
} else if (row == Conformers) {
return m_molecule->coordinate3dCount();
int offset = row - Bonds;
bool conformers = (m_molecule->coordinate3dCount() > 0);
bool residues = (m_molecule->residueCount() > 0);
if (conformers && offset == 0) {
return m_molecule->coordinate3dCount(); // conformers first
}
offset -= conformers ? 1 : 0; // tweak for conformer line
if (residues && offset == 0) {
return QVariant::fromValue(m_molecule->residueCount()); // residues next
}
offset -= residues ? 1 : 0; // tweak for residues line
/* TODO - chains
if (residues && offset == 0) {
return m_molecule->chainCount(); // chains next
}
*/

// now we're looping through the property map
const auto map = m_molecule->dataMap();
unsigned int howManyRows = row - 5; // tweak for residues, etc.
auto it = map.begin();
std::advance(it, offset);
if (it != map.end()) {
return QString::fromStdString(it->second.toString());
}

return QVariant();
}
Expand All @@ -151,24 +160,47 @@ QVariant MolecularModel::headerData(int section, Qt::Orientation orientation,
return tr("Value");
} else if (orientation == Qt::Vertical) {
if (section == Name)
return tr("Name");
return tr("Molecule Name");
else if (section == Mass)
return tr("Molar Mass (g/mol)");
return tr("Molecular Mass (g/mol)");
else if (section == Formula)
return tr("Formula");
return tr("Chemical Formula");
else if (section == Atoms)
return tr("Number of Atoms");
else if (section == Bonds)
return tr("Number of Bonds");

else
return QVariant();
int offset = section - Bonds;
bool conformers = (m_molecule->coordinate3dCount() > 0);
bool residues = (m_molecule->residueCount() > 0);
if (conformers && offset == 0) {
return tr("Coordinate Sets"); // conformers first
}
offset -= conformers ? 1 : 0; // tweak for conformer line
if (residues && offset == 0) {
return tr("Number of Residues");
}
offset -= residues ? 1 : 0; // tweak for residues line
/* TODO - chains
if (residues && offset == 0) {
return tr("Number of Chains");
}
*/

// now we're looping through the property map
const auto map = m_molecule->dataMap();
auto it = map.begin();
std::advance(it, offset);
if (it != map.end()) {
return QString::fromStdString(it->first);
}

return QVariant();

} else // row headers
return QVariant();
}

return QVariant();
return QVariant();
}

Qt::ItemFlags MolecularModel::flags(const QModelIndex& index) const
Expand All @@ -179,24 +211,6 @@ Qt::ItemFlags MolecularModel::flags(const QModelIndex& index) const
// return QAbstractItemModel::flags(index) | Qt::ItemIsEditable
// for the types and columns that can be edited
auto editable = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
if (m_type == AtomType) {
if (index.column() == AtomDataElement ||
index.column() == AtomDataFormalCharge || index.column() == AtomDataX ||
index.column() == AtomDataY || index.column() == AtomDataZ)
return editable;
// TODO: Color
} else if (m_type == BondType) {
if (index.column() == BondDataOrder || index.column() == BondDataLength)
return editable;
} else if (m_type == ResidueType) {
// TODO: Color
} else if (m_type == AngleType) {
if (index.column() == AngleDataValue)
return editable;
} else if (m_type == TorsionType) {
if (index.column() == TorsionDataValue)
return editable;
}

return QAbstractItemModel::flags(index);
}
Expand All @@ -210,106 +224,10 @@ bool MolecularModel::setData(const QModelIndex& index, const QVariant& value,
if (role != Qt::EditRole)
return false;

// If an item is actually editable, we should invalidate the cache
// We can still use the cached data -- we just invalidate now
// So that we can call "return" and have the cache invalid when we leave
m_validCache = false;
auto* undoMolecule = m_molecule->undoMolecule();

if (m_type == AtomType) {
Vector3 v = m_molecule->atomPosition3d(index.row());

switch (static_cast<AtomColumn>(index.column())) {
case AtomDataFormalCharge: {
bool ok;
int charge = value.toInt(&ok);
if (ok) {
undoMolecule->setFormalCharge(index.row(), charge);
}
break;
}
case AtomDataElement: { // atomic number
// Try first as a number
bool ok;
int atomicNumber = value.toInt(&ok);
if (ok)
undoMolecule->setAtomicNumber(index.row(), atomicNumber);
else {
// try a symbol
atomicNumber = Core::Elements::atomicNumberFromSymbol(
value.toString().toStdString());

if (atomicNumber != Avogadro::InvalidElement) {
undoMolecule->setAtomicNumber(index.row(), atomicNumber);
} else
return false;
} // not a number
break;
}
case AtomDataX:
v[0] = value.toDouble();
break;
case AtomDataY:
v[1] = value.toDouble();
break;
case AtomDataZ:
v[2] = value.toDouble();
break;
default:
return false;
}
undoMolecule->setAtomPosition3d(index.row(), v);

// cleanup atom changes
emit dataChanged(index, index);
m_molecule->emitChanged(Molecule::Atoms);
return true;
} else if (m_type == BondType) {
switch (static_cast<BondColumn>(index.column())) {
case BondDataOrder:
undoMolecule->setBondOrder(index.row(), value.toInt());
break;
case BondDataLength:
setBondLength(index.row(), value.toDouble());
break;
default:
return false;
}

emit dataChanged(index, index);
m_molecule->emitChanged(Molecule::Bonds);
return true;
} else if (m_type == AngleType) {
if (index.column() == AngleDataValue) {
setAngle(index.row(), value.toDouble());
emit dataChanged(index, index);
m_molecule->emitChanged(Molecule::Atoms);
return true;
}
} else if (m_type == TorsionType) {
if (index.column() == TorsionDataValue) {
setTorsion(index.row(), value.toDouble());
emit dataChanged(index, index);
m_molecule->emitChanged(Molecule::Atoms);
return true;
}
}

// TODO allow editing name
return false;
}

void MolecularModel::setMolecule(QtGui::Molecule* molecule)
{
if (molecule && molecule != m_molecule) {
m_molecule = molecule;

updateCache();

connect(m_molecule, SIGNAL(changed(unsigned int)), this,
SLOT(updateTable(unsigned int)));
}
}

void MolecularModel::updateTable(unsigned int flags)
{
if (flags & Molecule::Added || flags & Molecule::Removed) {
Expand Down
47 changes: 32 additions & 15 deletions avogadro/qtplugins/molecularproperties/molecularproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@
******************************************************************************/

#include "molecularproperties.h"

#include "molecularpropertiesdialog.h"
#include "molecularview.h"

#include <QAction>

#include <QStringList>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QVBoxLayout>

namespace Avogadro::QtPlugins {

MolecularProperties::MolecularProperties(QObject* parent_)
: Avogadro::QtGui::ExtensionPlugin(parent_), m_action(new QAction(this)),
m_dialog(nullptr), m_molecule(nullptr)
m_molecule(nullptr)
{
m_action->setEnabled(true);
m_action->setText(tr("&Molecular…"));
Expand All @@ -24,9 +26,7 @@ MolecularProperties::MolecularProperties(QObject* parent_)
connect(m_action, SIGNAL(triggered()), SLOT(showDialog()));
}

MolecularProperties::~MolecularProperties()
{
}
MolecularProperties::~MolecularProperties() {}

QString MolecularProperties::description() const
{
Expand All @@ -49,17 +49,34 @@ void MolecularProperties::setMolecule(QtGui::Molecule* mol)
return;

m_molecule = mol;
if (m_dialog)
m_dialog->setMolecule(m_molecule);
}

void MolecularProperties::showDialog()
{
if (!m_dialog) {
m_dialog = new MolecularPropertiesDialog(
m_molecule, qobject_cast<QWidget*>(this->parent()));
}
m_dialog->show();
// copied from the propeties dialog
auto* dialog = new QDialog(qobject_cast<QWidget*>(parent()));
auto* layout = new QVBoxLayout(dialog);
dialog->setLayout(layout);
// Don't show whitespace around the table view
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);

auto* model = new MolecularModel();
model->setMolecule(m_molecule);
// view will delete itself & model using deleteLater()
auto* view = new MolecularView(dialog);
view->setMolecule(m_molecule);
view->setSourceModel(model);
view->setModel(model);

view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->resizeColumnsToContents();

layout->addWidget(view);

dialog->setWindowTitle(view->windowTitle());
dialog->setWindowFlags(Qt::Window);
dialog->show();
}

} // namespace Avogadro
} // namespace Avogadro::QtPlugins
2 changes: 0 additions & 2 deletions avogadro/qtplugins/molecularproperties/molecularproperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class Molecule;
}

namespace QtPlugins {
class MolecularPropertiesDialog;

/**
* @brief The MolecularProperties class is an extension to launch
Expand All @@ -41,7 +40,6 @@ private slots:

private:
QAction* m_action;
MolecularPropertiesDialog* m_dialog;
QtGui::Molecule* m_molecule;
};

Expand Down
Loading

1 comment on commit a90aa27

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERROR: clang-format-diff detected formatting issues. See the artifact for a patch or run clang-format on your branch.

Please sign in to comment.