diff --git a/compoundviewer/CMakeLists.txt b/compoundviewer/CMakeLists.txt index 54734a02..db367a7d 100644 --- a/compoundviewer/CMakeLists.txt +++ b/compoundviewer/CMakeLists.txt @@ -1,36 +1,38 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR}/.. ${EIGEN3_INCLUDE_DIR} ${OPENBABEL2_INCLUDE_DIR} ${AvogadroLibs_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/compoundviewer ) set(compoundviewer_SRCS iowrapper.cpp kalziumglwidget.cpp ) #add_subdirectory(widgets) add_library(compoundviewer ${compoundviewer_SRCS}) target_link_libraries(compoundviewer KF5::I18n KF5::WidgetsAddons KF5::CoreAddons Qt5::OpenGL Qt5::Gui Qt5::Widgets ${OPENBABEL2_LIBRARIES} AvogadroQtGui AvogadroQtOpenGL AvogadroQtPlugins ) set_target_properties(compoundviewer PROPERTIES VERSION ${KALZIUMLIB_VERSION} SOVERSION ${KALZIUMLIB_SOVERSION} + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON ) install(TARGETS compoundviewer ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/compoundviewer/iowrapper.cpp b/compoundviewer/iowrapper.cpp index 2552ebd4..2fae8823 100644 --- a/compoundviewer/iowrapper.cpp +++ b/compoundviewer/iowrapper.cpp @@ -1,74 +1,98 @@ /*************************************************************************** * Copyright (C) 2006 by Carsten Niehaus * Copyright (C) 2007-2008 by Marcus D. Hanwell * Copyright (C) 2016 by Andreas Cord-Landwehr ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "iowrapper.h" #include #include #include #include +#include +#include #include #include +#include #include #include #include -Avogadro::Core::Molecule * IoWrapper::readMolecule(const QString &filename) +std::unique_ptr IoWrapper::readMolecule(const QString &filename) { std::ifstream inFileStream(QFile::encodeName(filename).constData()); if (!inFileStream) { QMessageBox::warning(nullptr, i18n("Problem while opening the file"), i18n("Cannot open the specified file.")); return nullptr; } - auto mol = new Avogadro::Core::Molecule; - Avogadro::Io::CmlFormat cmlFormat; - if (!cmlFormat.read(inFileStream, *mol)) { - qCritical() << "Could not read file:" << filename; + auto mol = std::make_unique(); + auto format = getFileReader(QFileInfo(filename).suffix()); + + if (!format){ + qCritical() << "Could not initialize file reader for file " << filename; + return nullptr; + } + + if (!format->read(inFileStream, *mol)) { + qCritical() << "Could not read file " << filename << "; Error message: " + << QString().fromStdString(format->error()); return nullptr; } + return mol; } bool IoWrapper::writeMolecule(const QString &filename, Avogadro::Core::Molecule *mol) { std::ofstream outFileStream(QFile::encodeName(filename).constData()); if (!outFileStream) { QMessageBox::warning(nullptr, i18n("Sorry"), i18n("Cannot save to the specified file.")); return false; } Avogadro::Io::CmlFormat cmlFormat; if (!cmlFormat.write(outFileStream, *mol)) { qCritical() << "Could not read file:" << filename; return false; } return true; } QString IoWrapper::getFormula(Avogadro::QtGui::Molecule *molecule) { return QString::fromStdString(molecule->formula()); } QString IoWrapper::getPrettyFormula(Avogadro::QtGui::Molecule *molecule) { QString formula = QString::fromStdString(molecule->formula()); formula.replace(QRegularExpression("(\\d+)"), "\\1"); return formula; } + +std::unique_ptr IoWrapper::getFileReader(const QString &format) +{ + if (format == QStringLiteral("cml")) { + return std::make_unique(); + } else if (format == QStringLiteral("pdb")) { + return std::make_unique(); + } else if (format == QStringLiteral("xyz")) { + return std::make_unique(); + } else { + return nullptr; + } +} diff --git a/compoundviewer/iowrapper.h b/compoundviewer/iowrapper.h index 8746f383..fd434b81 100644 --- a/compoundviewer/iowrapper.h +++ b/compoundviewer/iowrapper.h @@ -1,42 +1,51 @@ /*************************************************************************** * Copyright (C) 2006 by Carsten Niehaus * Copyright (C) 2007-2008 by Marcus D. Hanwell * Copyright (C) 2016 by Andreas Cord-Landwehr ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef IOWRAPPER_H #define IOWRAPPER_H +#include #include #include +#include + /** * @author Carsten Niehaus */ class COMPOUNDVIEWER_EXPORT IoWrapper { public: /** * This class reads the molecule in the file @p filename. It returns 0 if * the file couldn't be read. */ - static Avogadro::Core::Molecule * readMolecule(const QString &filename); + static std::unique_ptr readMolecule(const QString &filename); static bool writeMolecule(const QString& filename, Avogadro::Core::Molecule *); static QString getFormula(Avogadro::QtGui::Molecule *molecule); static QString getPrettyFormula(Avogadro::QtGui::Molecule *molecule); + +private: + /** + * Get file format reader appropriate for the file format + */ + static std::unique_ptr getFileReader(const QString &format); }; #endif diff --git a/src/tools/moleculeview.cpp b/src/tools/moleculeview.cpp index 39d83b01..4710f7e0 100644 --- a/src/tools/moleculeview.cpp +++ b/src/tools/moleculeview.cpp @@ -1,336 +1,343 @@ /*************************************************************************** * Copyright (C) 2006 by Carsten Niehaus * Copyright (C) 2007-2008 by Marcus D. Hanwell * Copyright (C) 2016 by Andreas Cord-Landwehr ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "moleculeview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iowrapper.h" #include #include // This is needed to ensure that the forcefields are set up right with GCC vis #ifdef __KDE_HAVE_GCC_VISIBILITY #define HAVE_GCC_VISIBILITY #endif #include #include #include #include #include #include #include #include using namespace OpenBabel; using namespace Avogadro::QtGui; MoleculeDialog::MoleculeDialog(QWidget * parent) : QDialog(parent) , m_path(QString()) , m_periodicTable(nullptr) { // use multi-sample (anti-aliased) OpenGL if available QGLFormat defFormat = QGLFormat::defaultFormat(); defFormat.setSampleBuffers(true); QGLFormat::setDefaultFormat(defFormat); setWindowTitle(i18n("Molecular Editor")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); QPushButton *user1Button = new QPushButton; buttonBox->addButton(user1Button, QDialogButtonBox::ActionRole); QPushButton *user2Button = new QPushButton; buttonBox->addButton(user2Button, QDialogButtonBox::ActionRole); QPushButton *user3Button = new QPushButton; buttonBox->addButton(user3Button, QDialogButtonBox::ActionRole); connect(buttonBox, &QDialogButtonBox::rejected, this, &MoleculeDialog::reject); user1Button->setDefault(true); KGuiItem::assign(user1Button, KGuiItem(i18n("Load Molecule"))); KGuiItem::assign(user2Button, KGuiItem(i18n("Download New Molecules"))); KGuiItem::assign(user3Button, KGuiItem(i18n("Save Molecule"))); ui.setupUi(mainWidget); // Attempt to set up the UFF forcefield // m_forceField = OBForceField::FindForceField("UFF"); // if (!m_forceField) { // ui.optimizeButton->setEnabled(false); // } ui.styleCombo->addItems({"Ball and Stick", "Licorice", "Van der Waals","Van der Waals (AO)", "Wireframe"}); connect(ui.styleCombo, static_cast(&QComboBox::currentIndexChanged), this, &MoleculeDialog::slotUpdateScenePlugin); slotUpdateScenePlugin(); connect(ui.tabWidget, &QTabWidget::currentChanged, this, &MoleculeDialog::setViewEdit); // Editing parameters // commented out until we find new API for pumbling to OpenBabel // connect(ui.optimizeButton, &QPushButton::clicked, // this, &MoleculeDialog::slotGeometryOptimize); connect(ui.clearDrawingButton, &QPushButton::clicked, this, &MoleculeDialog::clearAllElementsInEditor); connect(ui.glWidget->molecule(), &Avogadro::QtGui::Molecule::changed, this, &MoleculeDialog::slotUpdateStatistics); connect(user1Button, &QPushButton::clicked, this, &MoleculeDialog::slotLoadMolecule); connect(user2Button, &QPushButton::clicked, this, &MoleculeDialog::slotDownloadNewStuff); connect(user3Button, &QPushButton::clicked, this, &MoleculeDialog::slotSaveMolecule); mainLayout->addWidget(buttonBox); // Check that we have managed to load up some tools and engines int nTools = ui.glWidget->tools().size(); if (!nTools) { QString error = i18n("No tools loaded - it is likely that the Avogadro plugins could not be located."); KMessageBox::error(this, error, i18n("Kalzium")); } // objectName is also used in Avogadro2 for identifying tools foreach (auto *tool, ui.glWidget->tools()) { if (tool->objectName() == QLatin1String("Editor")) { ui.editTabLayout->insertWidget(0, tool->toolWidget()); break; } } } void MoleculeDialog::slotLoadMolecule() { // Check that we have managed to load up some tools and engines int nTools = ui.glWidget->tools().size(); if (!nTools) { QString error = i18n("No tools loaded - it is likely that the Avogadro plugins could not be located. " "No molecules can be viewed until this issue is resolved."); KMessageBox::information(this, error); } m_path = QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("data/molecules/"), QStandardPaths::LocateDirectory); QString commonMoleculeFormats = i18n("Common molecule formats"); QString allFiles = i18n("All files"); QString filename = QFileDialog::getOpenFileName( this, i18n("Choose a file to open"), m_path, commonMoleculeFormats + "(*.cml *.xyz *.ent *.pdb *.alc *.chm *.cdx *.cdxml *.c3d1 *.c3d2" " *.gpr *.mdl *.mol *.sdf *.sd *.crk3d *.cht *.dmol *.bgf" " *.gam *.inp *.gamin *.gamout *.tmol *.fract" " *.mpd *.mol2);;" + allFiles + "(*)"); loadMolecule(filename); } void MoleculeDialog::slotUpdateScenePlugin() { const QString text = ui.styleCombo->currentText(); for (int i = 0; i < ui.glWidget->sceneModel().rowCount(QModelIndex()); ++i) { QModelIndex index = ui.glWidget->sceneModel().index(i, 0); if (text == ui.glWidget->sceneModel().data(index, Qt::DisplayRole)) { ui.glWidget->sceneModel().setData(index, Qt::Checked, Qt::CheckStateRole); } else { ui.glWidget->sceneModel().setData(index, Qt::Unchecked, Qt::CheckStateRole); } } } void MoleculeDialog::loadMolecule(const QString &filename) { if (filename.isEmpty()) { return; } // 1. workaround for missing copy-constructor: fixed in Avogadro2 > 0.9 // 2. another workaround for broken copy-constructor that does not // initialize the m_undoMolecule private member variable; // this molecule should be created on the heap instead of the stack - m_molecule = *IoWrapper::readMolecule(filename); + auto molecule_ptr = IoWrapper::readMolecule(filename); + if (!molecule_ptr) + { + KMessageBox::error(this, i18n("Could not load molecule"), i18n("Loading the molecule failed.")); + return; + } + + m_molecule = *molecule_ptr; if (m_molecule.atomCount() != 0) { disconnect(ui.glWidget->molecule(), 0, this, 0); ui.glWidget->setMolecule(&m_molecule); ui.glWidget->update(); slotUpdateStatistics(); connect(&m_molecule, &Avogadro::QtGui::Molecule::changed, this, &MoleculeDialog::slotUpdateStatistics); } ui.glWidget->resetCamera(); ui.glWidget->updateScene(); } void MoleculeDialog::clearAllElementsInEditor() { ui.glWidget->molecule()->clearBonds(); ui.glWidget->molecule()->clearAtoms(); ui.glWidget->updateScene(); } void MoleculeDialog::slotSaveMolecule() { QString commonMoleculeFormats = i18n("Common molecule formats"); QString allFiles = i18n("All files"); QString filename = QFileDialog::getSaveFileName(this, i18n("Choose a file to save to"), QString(), commonMoleculeFormats + QStringLiteral(" (*.cml *.xyz *.ent *.pdb *.alc *.chm *.cdx *.cdxml *.c3d1 *.c3d2" " *.gpr *.mdl *.mol *.sdf *.sd *.crk3d *.cht *.dmol *.bgf" " *.gam *.inp *.gamin *.gamout *.tmol *.fract" " *.mpd *.mol2);;") + allFiles + QStringLiteral(" (*)") ); if (!filename.contains(QLatin1String("."))) { filename.append(QLatin1String(".cml")); } IoWrapper io; io.writeMolecule(filename, ui.glWidget->molecule()); } void MoleculeDialog::setViewEdit(int mode) { if (mode == 0) { ui.glWidget->setActiveTool(QStringLiteral("Navigator")); } else if (mode == 1) { ui.glWidget->setActiveTool(QStringLiteral("Editor")); } else if (mode == 2) { ui.glWidget->setActiveTool(QStringLiteral("MeasureTool")); } } MoleculeDialog::~MoleculeDialog() { } void MoleculeDialog::slotUpdateStatistics() { Molecule* mol = ui.glWidget->molecule(); if (!mol) { return; } const std::string name = mol->data(QStringLiteral("name").toStdString()).toString(); ui.nameLabel->setText(QString::fromStdString(name)); ui.weightLabel->setText(i18nc("This 'u' stands for the chemical unit (u for 'units'). Most likely this does not need to be translated at all!", "%1 u", mol->mass())); ui.formulaLabel->setText(IoWrapper::getPrettyFormula(mol)); ui.glWidget->update(); } void MoleculeDialog::slotDownloadNewStuff() { qDebug() << "Kalzium new stuff"; KNS3::DownloadDialog dialog(this); dialog.exec(); // list of changed entries QString destinationDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QDir dir(destinationDir); if (!dir.exists()) { destinationDir = QDir::homePath(); } bool anyError = false; bool anySuccess = false; bool moreOneInstalledFile = false; QString exactlyOneFile; foreach (const KNS3::Entry& entry, dialog.changedEntries()) { // care only about installed ones if (entry.status() == KNS3::Entry::Installed) { qDebug() << "Changed Entry: " << entry.installedFiles(); foreach (const QString &origFile, entry.installedFiles()) { const QString destFile = destinationDir + '/' + QFileInfo(origFile).fileName(); KJob *job = KIO::file_move(QUrl::fromLocalFile(origFile), QUrl::fromLocalFile(destFile));; const bool success = job->exec(); if (success) { if (exactlyOneFile.isEmpty()) { exactlyOneFile = destFile; } else { moreOneInstalledFile = true; } anySuccess = true; } else { KMessageBox::error(this, i18n("Failed to download molecule %1 to %2.", entry.name(), destFile)); anyError = true; } } } } if (anySuccess) { if (anyError) { KMessageBox::information(this, i18n("The molecules that could be downloaded have been saved to %1.", destinationDir)); } else { KMessageBox::information(this, i18n("The molecules have been saved to %1.", destinationDir)); } if (!moreOneInstalledFile) { loadMolecule(exactlyOneFile); } } } //TODO there is currently no API to perform the necessary OpenBabel-Avogadro // conversions, after the migration to Avogadro2; at least with v0.9 void MoleculeDialog::slotGeometryOptimize() { // // Perform a geometry optimization // if (!m_forceField) { // return; // } // // Molecule* molecule = ui.glWidget->molecule(); // OpenBabel::OBMol obmol;//(molecule->OBMol()); // // // Warn the user if the force field cannot be set up for the molecule // if (!m_forceField->Setup(obmol)) { // KMessageBox::error(this, // i18n("Could not set up force field for this molecule"), // i18n("Kalzium")); // return; // } // // // Reasonable default values for most users // m_forceField->SteepestDescentInitialize(500, 1.0e-5); // // Provide some feedback as the optimization runs // while (m_forceField->SteepestDescentTakeNSteps(5)) { // m_forceField->UpdateCoordinates(obmol); // molecule->setOBMol(&obmol); // molecule->update(); // } }