Warning, file /education/kalzium/src/tools/moleculeview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2006 Carsten Niehaus <cniehaus@kde.org> 0003 SPDX-FileCopyrightText: 2007-2008 Marcus D. Hanwell <marcus@cryos.org> 0004 SPDX-FileCopyrightText: 2016 Andreas Cord-Landwehr <cordlandwehr@kde.org> 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "moleculeview.h" 0009 0010 #include <avogadro/qtgui/elementtranslator.h> 0011 #include <avogadro/qtgui/periodictableview.h> 0012 #include <avogadro/qtgui/scenepluginmodel.h> 0013 #include <avogadro/qtgui/toolplugin.h> 0014 0015 #include <QDebug> 0016 #include <QFileInfo> 0017 #include <QGLFormat> 0018 #include <QUrl> 0019 0020 #include <KIO/Job> 0021 #include <KJob> 0022 #include <KLocalizedString> 0023 #include <KMessageBox> 0024 #include <KNS3/DownloadDialog> 0025 0026 #include "iowrapper.h" 0027 0028 #include <openbabel/mol.h> 0029 #include <openbabel/obiter.h> 0030 // This is needed to ensure that the forcefields are set up right with GCC vis 0031 #ifdef __KDE_HAVE_GCC_VISIBILITY 0032 #define HAVE_GCC_VISIBILITY 0033 #endif 0034 #include <KGuiItem> 0035 #include <QDialogButtonBox> 0036 #include <QFileDialog> 0037 #include <QPushButton> 0038 #include <QStandardPaths> 0039 #include <QVBoxLayout> 0040 #include <openbabel/forcefield.h> 0041 0042 using namespace OpenBabel; 0043 using namespace Avogadro::QtGui; 0044 0045 MoleculeDialog::MoleculeDialog(QWidget *parent) 0046 : QDialog(parent) 0047 , m_path(QString()) 0048 , m_periodicTable(nullptr) 0049 { 0050 // use multi-sample (anti-aliased) OpenGL if available 0051 QGLFormat defFormat = QGLFormat::defaultFormat(); 0052 defFormat.setSampleBuffers(true); 0053 QGLFormat::setDefaultFormat(defFormat); 0054 0055 setWindowTitle(i18nc("@title:window", "Molecular Editor")); 0056 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0057 QWidget *mainWidget = new QWidget(this); 0058 QVBoxLayout *mainLayout = new QVBoxLayout; 0059 setLayout(mainLayout); 0060 mainLayout->addWidget(mainWidget); 0061 QPushButton *user1Button = new QPushButton; 0062 buttonBox->addButton(user1Button, QDialogButtonBox::ActionRole); 0063 QPushButton *user2Button = new QPushButton; 0064 buttonBox->addButton(user2Button, QDialogButtonBox::ActionRole); 0065 QPushButton *user3Button = new QPushButton; 0066 buttonBox->addButton(user3Button, QDialogButtonBox::ActionRole); 0067 connect(buttonBox, &QDialogButtonBox::rejected, this, &MoleculeDialog::reject); 0068 0069 user1Button->setDefault(true); 0070 0071 KGuiItem::assign(user1Button, KGuiItem(i18n("Load Molecule"))); 0072 0073 KGuiItem::assign(user2Button, KGuiItem(i18n("Download New Molecules"))); 0074 0075 KGuiItem::assign(user3Button, KGuiItem(i18n("Save Molecule"))); 0076 0077 ui.setupUi(mainWidget); 0078 0079 // Attempt to set up the UFF forcefield 0080 // m_forceField = OBForceField::FindForceField("UFF"); 0081 // if (!m_forceField) { 0082 // ui.optimizeButton->setEnabled(false); 0083 // } 0084 0085 ui.styleCombo->addItems({"Ball and Stick", "Licorice", "Van der Waals", "Van der Waals (AO)", "Wireframe"}); 0086 connect(ui.styleCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MoleculeDialog::slotUpdateScenePlugin); 0087 slotUpdateScenePlugin(); 0088 0089 connect(ui.tabWidget, &QTabWidget::currentChanged, this, &MoleculeDialog::setViewEdit); 0090 0091 // Editing parameters 0092 // commented out until we find new API for pumbling to OpenBabel 0093 // connect(ui.optimizeButton, &QPushButton::clicked, 0094 // this, &MoleculeDialog::slotGeometryOptimize); 0095 connect(ui.clearDrawingButton, &QPushButton::clicked, this, &MoleculeDialog::clearAllElementsInEditor); 0096 0097 connect(ui.glWidget->molecule(), &Avogadro::QtGui::Molecule::changed, this, &MoleculeDialog::slotUpdateStatistics); 0098 0099 connect(user1Button, &QPushButton::clicked, this, &MoleculeDialog::slotLoadMolecule); 0100 connect(user2Button, &QPushButton::clicked, this, &MoleculeDialog::slotDownloadNewStuff); 0101 connect(user3Button, &QPushButton::clicked, this, &MoleculeDialog::slotSaveMolecule); 0102 0103 mainLayout->addWidget(buttonBox); 0104 0105 // Check that we have managed to load up some tools and engines 0106 int nTools = ui.glWidget->tools().size(); 0107 if (!nTools) { 0108 QString error = i18n("No tools loaded - it is likely that the Avogadro plugins could not be located."); 0109 // use parent as parent for the messagebix, as this dialog is not shown yet 0110 // and thus not known to the window system to position to dialog relative to it 0111 KMessageBox::error(parent, error, i18n("Kalzium")); 0112 } 0113 0114 // objectName is also used in Avogadro2 for identifying tools 0115 foreach (auto *tool, ui.glWidget->tools()) { 0116 if (tool->objectName() == QLatin1String("Editor")) { 0117 ui.editTabLayout->insertWidget(0, tool->toolWidget()); 0118 break; 0119 } 0120 } 0121 } 0122 0123 void MoleculeDialog::slotLoadMolecule() 0124 { 0125 // Check that we have managed to load up some tools and engines 0126 int nTools = ui.glWidget->tools().size(); 0127 0128 if (!nTools) { 0129 QString error = i18n( 0130 "No tools loaded - it is likely that the Avogadro plugins could not be located. " 0131 "No molecules can be viewed until this issue is resolved."); 0132 KMessageBox::information(this, error); 0133 } 0134 0135 m_path = QStandardPaths::locate(QStandardPaths::AppLocalDataLocation, QStringLiteral("data/molecules/"), QStandardPaths::LocateDirectory); 0136 0137 QString commonMoleculeFormats = i18n("Common molecule formats"); 0138 QString allFiles = i18n("All files"); 0139 0140 QString filename = QFileDialog::getOpenFileName( 0141 this, 0142 i18n("Choose a file to open"), 0143 m_path, 0144 commonMoleculeFormats + "(*.cml *.xyz *.ent *.pdb *.alc *.chm *.cdx *.cdxml *.c3d1 *.c3d2" 0145 " *.gpr *.mdl *.mol *.sdf *.sd *.crk3d *.cht *.dmol *.bgf" 0146 " *.gam *.inp *.gamin *.gamout *.tmol *.fract" 0147 " *.mpd *.mol2);;" + allFiles + "(*)"); 0148 0149 loadMolecule(filename); 0150 } 0151 0152 void MoleculeDialog::slotUpdateScenePlugin() 0153 { 0154 const QString text = ui.styleCombo->currentText(); 0155 for (int i = 0; i < ui.glWidget->sceneModel().rowCount(QModelIndex()); ++i) { 0156 QModelIndex index = ui.glWidget->sceneModel().index(i, 0); 0157 if (text == ui.glWidget->sceneModel().data(index, Qt::DisplayRole)) { 0158 ui.glWidget->sceneModel().setData(index, Qt::Checked, Qt::CheckStateRole); 0159 } else { 0160 ui.glWidget->sceneModel().setData(index, Qt::Unchecked, Qt::CheckStateRole); 0161 } 0162 } 0163 } 0164 0165 void MoleculeDialog::loadMolecule(const QString &filename) 0166 { 0167 if (filename.isEmpty()) { 0168 return; 0169 } 0170 0171 // 1. workaround for missing copy-constructor: fixed in Avogadro2 > 0.9 0172 // 2. another workaround for broken copy-constructor that does not 0173 // initialize the m_undoMolecule private member variable; 0174 // this molecule should be created on the heap instead of the stack 0175 auto molecule_ptr = IoWrapper::readMolecule(filename); 0176 if (!molecule_ptr) { 0177 KMessageBox::error(this, i18n("Could not load molecule"), i18n("Loading the molecule failed.")); 0178 return; 0179 } 0180 0181 m_molecule = *molecule_ptr; 0182 0183 if (m_molecule.atomCount() != 0) { 0184 disconnect(ui.glWidget->molecule(), nullptr, this, nullptr); 0185 ui.glWidget->setMolecule(&m_molecule); 0186 ui.glWidget->update(); 0187 slotUpdateStatistics(); 0188 connect(&m_molecule, &Avogadro::QtGui::Molecule::changed, this, &MoleculeDialog::slotUpdateStatistics); 0189 } 0190 ui.glWidget->resetCamera(); 0191 ui.glWidget->updateScene(); 0192 } 0193 0194 void MoleculeDialog::clearAllElementsInEditor() 0195 { 0196 ui.glWidget->molecule()->clearBonds(); 0197 ui.glWidget->molecule()->clearAtoms(); 0198 ui.glWidget->updateScene(); 0199 } 0200 0201 void MoleculeDialog::slotSaveMolecule() 0202 { 0203 QString commonMoleculeFormats = i18n("Common molecule formats"); 0204 QString allFiles = i18n("All files"); 0205 QString filename = QFileDialog::getSaveFileName(this, 0206 i18n("Choose a file to save to"), 0207 QString(), 0208 commonMoleculeFormats 0209 + QStringLiteral(" (*.cml *.xyz *.ent *.pdb *.alc *.chm *.cdx *.cdxml *.c3d1 *.c3d2" 0210 " *.gpr *.mdl *.mol *.sdf *.sd *.crk3d *.cht *.dmol *.bgf" 0211 " *.gam *.inp *.gamin *.gamout *.tmol *.fract" 0212 " *.mpd *.mol2);;") 0213 + allFiles + QStringLiteral(" (*)")); 0214 0215 if (!filename.contains(QLatin1String("."))) { 0216 filename.append(QLatin1String(".cml")); 0217 } 0218 0219 IoWrapper io; 0220 io.writeMolecule(filename, ui.glWidget->molecule()); 0221 } 0222 0223 void MoleculeDialog::setViewEdit(int mode) 0224 { 0225 if (mode == 0) { 0226 ui.glWidget->setActiveTool(QStringLiteral("Navigator")); 0227 } else if (mode == 1) { 0228 ui.glWidget->setActiveTool(QStringLiteral("Editor")); 0229 } else if (mode == 2) { 0230 ui.glWidget->setActiveTool(QStringLiteral("MeasureTool")); 0231 } 0232 } 0233 0234 MoleculeDialog::~MoleculeDialog() 0235 { 0236 } 0237 0238 void MoleculeDialog::slotUpdateStatistics() 0239 { 0240 Molecule *mol = ui.glWidget->molecule(); 0241 if (!mol) { 0242 return; 0243 } 0244 const std::string name = mol->data(QStringLiteral("name").toStdString()).toString(); 0245 ui.nameLabel->setText(QString::fromStdString(name)); 0246 ui.weightLabel->setText( 0247 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())); 0248 ui.formulaLabel->setText(IoWrapper::getPrettyFormula(mol)); 0249 ui.glWidget->update(); 0250 } 0251 0252 void MoleculeDialog::slotDownloadNewStuff() 0253 { 0254 qDebug() << "Kalzium new stuff"; 0255 0256 KNS3::DownloadDialog dialog(this); 0257 dialog.exec(); 0258 // list of changed entries 0259 QString destinationDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); 0260 QDir dir(destinationDir); 0261 if (!dir.exists()) { 0262 destinationDir = QDir::homePath(); 0263 } 0264 bool anyError = false; 0265 bool anySuccess = false; 0266 bool moreOneInstalledFile = false; 0267 QString exactlyOneFile; 0268 foreach (const KNS3::Entry &entry, dialog.changedEntries()) { 0269 // care only about installed ones 0270 if (entry.status() == KNS3::Entry::Installed) { 0271 qDebug() << "Changed Entry: " << entry.installedFiles(); 0272 foreach (const QString &origFile, entry.installedFiles()) { 0273 const QString destFile = destinationDir + '/' + QFileInfo(origFile).fileName(); 0274 KJob *job = KIO::file_move(QUrl::fromLocalFile(origFile), QUrl::fromLocalFile(destFile)); 0275 ; 0276 const bool success = job->exec(); 0277 if (success) { 0278 if (exactlyOneFile.isEmpty()) { 0279 exactlyOneFile = destFile; 0280 } else { 0281 moreOneInstalledFile = true; 0282 } 0283 anySuccess = true; 0284 } else { 0285 KMessageBox::error(this, i18n("Failed to download molecule %1 to %2.", entry.name(), destFile)); 0286 anyError = true; 0287 } 0288 } 0289 } 0290 } 0291 if (anySuccess) { 0292 if (anyError) { 0293 KMessageBox::information(this, i18n("The molecules that could be downloaded have been saved to %1.", destinationDir)); 0294 } else { 0295 KMessageBox::information(this, i18n("The molecules have been saved to %1.", destinationDir)); 0296 } 0297 if (!moreOneInstalledFile) { 0298 loadMolecule(exactlyOneFile); 0299 } 0300 } 0301 } 0302 0303 // TODO there is currently no API to perform the necessary OpenBabel-Avogadro 0304 // conversions, after the migration to Avogadro2; at least with v0.9 0305 void MoleculeDialog::slotGeometryOptimize() 0306 { 0307 // // Perform a geometry optimization 0308 // if (!m_forceField) { 0309 // return; 0310 // } 0311 // 0312 // Molecule* molecule = ui.glWidget->molecule(); 0313 // OpenBabel::OBMol obmol;//(molecule->OBMol()); 0314 // 0315 // // Warn the user if the force field cannot be set up for the molecule 0316 // if (!m_forceField->Setup(obmol)) { 0317 // KMessageBox::error(this, 0318 // i18n("Could not set up force field for this molecule"), 0319 // i18n("Kalzium")); 0320 // return; 0321 // } 0322 // 0323 // // Reasonable default values for most users 0324 // m_forceField->SteepestDescentInitialize(500, 1.0e-5); 0325 // // Provide some feedback as the optimization runs 0326 // while (m_forceField->SteepestDescentTakeNSteps(5)) { 0327 // m_forceField->UpdateCoordinates(obmol); 0328 // molecule->setOBMol(&obmol); 0329 // molecule->update(); 0330 // } 0331 }