From c9c992a95c689abc9fec0b7873cddf27b6401b06 Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Fri, 29 Nov 2024 20:26:59 -0500 Subject: [PATCH 1/3] When possible, load script names from cache Slight speedup and removes some debugging messages Signed-off-by: Geoff Hutchison --- avogadro/qtgui/scriptloader.cpp | 43 ++++++++++++++++++- .../qtplugins/quantuminput/quantuminput.cpp | 2 +- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/avogadro/qtgui/scriptloader.cpp b/avogadro/qtgui/scriptloader.cpp index 9b266c404a..86d575e048 100644 --- a/avogadro/qtgui/scriptloader.cpp +++ b/avogadro/qtgui/scriptloader.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace Avogadro::QtGui { @@ -42,6 +43,14 @@ QMultiMap ScriptLoader::scriptList(const QString& type) QStringList dirs; QMultiMap scriptList; + QSettings settings; // to cache the names of scripts + QStringList scriptFiles = settings.value("scripts/" + type).toStringList(); + QStringList scriptNames = + settings.value("scripts/" + type + "/names").toStringList(); + // hash from the last modified time and size of the scripts + QStringList scriptHashes = + settings.value("scripts/" + type + "/hashes").toStringList(); + // add the default paths QStringList stdPaths = QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation); @@ -56,7 +65,9 @@ QMultiMap ScriptLoader::scriptList(const QString& type) // build up a list of possible files, then we check if they're real scripts QStringList fileList; foreach (const QString& dirStr, dirs) { +#ifndef NDEBUG qDebug() << tr("Checking for %1 scripts in path %2").arg(type).arg(dirStr); +#endif QDir dir(dirStr); if (dir.exists() && dir.isReadable()) { foreach ( @@ -118,6 +129,21 @@ QMultiMap ScriptLoader::scriptList(const QString& type) // go through the list of files to see if they're actually scripts foreach (const QString& filePath, fileList) { + QFileInfo file(filePath); + // check if we have this from the last time + if (scriptFiles.contains(filePath)) { + int index = scriptFiles.indexOf(filePath); + if (index != -1) { + QString hash = scriptHashes.at(index); + // got a match? + if (hash == + QString::number(file.size()) + file.lastModified().toString()) { + scriptList.insert(scriptNames.at(index), filePath); + continue; + } + } + } + QString displayName; if (queryProgramName(filePath, displayName)) { if (displayName.isEmpty()) @@ -126,14 +152,27 @@ QMultiMap ScriptLoader::scriptList(const QString& type) // Might be another script with the same name if (scriptList.contains(displayName)) { // check the last-modified-time of the existing case - QFileInfo file(filePath); QFileInfo existingFile(scriptList.value(displayName)); if (file.lastModified() > existingFile.lastModified()) { // replace existing with this new entry scriptList.replace(displayName, filePath); + // update the cache + int index = scriptFiles.indexOf(filePath); + if (index != -1) { + scriptFiles.replace(index, filePath); + scriptNames.replace(index, displayName); + scriptHashes.replace(index, QString::number(file.size()) + + file.lastModified().toString()); + } } - } else // new entry + } else { // new entry scriptList.insert(displayName, filePath); + // update the cache + scriptFiles << filePath; + scriptNames << displayName; + scriptHashes << QString::number(file.size()) + + file.lastModified().toString(); + } } // run queryProgramName } // foreach files diff --git a/avogadro/qtplugins/quantuminput/quantuminput.cpp b/avogadro/qtplugins/quantuminput/quantuminput.cpp index d8d1a9208e..2c15d79a78 100644 --- a/avogadro/qtplugins/quantuminput/quantuminput.cpp +++ b/avogadro/qtplugins/quantuminput/quantuminput.cpp @@ -154,7 +154,7 @@ void QuantumInput::updateActions() // Include the full path if there are multiple generators with the same // name. QString label = programName; - if (!label.endsWith("…") && !label.endsWith("…")) + if (!label.endsWith("…") && !label.endsWith("...")) label.append("…"); if (scripts.size() == 1) { From c18cf3a873c90cebce249f3872465c1647301b3c Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Fri, 29 Nov 2024 20:40:47 -0500 Subject: [PATCH 2/3] Cache command menu paths Signed-off-by: Geoff Hutchison --- avogadro/qtplugins/commandscripts/command.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/avogadro/qtplugins/commandscripts/command.cpp b/avogadro/qtplugins/commandscripts/command.cpp index 5bac9077b0..0cc58778a5 100644 --- a/avogadro/qtplugins/commandscripts/command.cpp +++ b/avogadro/qtplugins/commandscripts/command.cpp @@ -65,6 +65,17 @@ QStringList Command::menuPath(QAction* action) const return path; } + // cache the menu paths + QSettings settings; + QFileInfo info(scriptFileName); // check if the script matches the hash + QString hash = + settings.value("scripts/" + scriptFileName + "/hash").toString(); + if (hash == QString::number(info.size()) + info.lastModified().toString()) { + path = settings.value("scripts/" + scriptFileName + "/menu").toStringList(); + if (!path.isEmpty()) + return path; + } + // otherwise, we have a script name, so ask it InterfaceScript gen(scriptFileName); path = gen.menuPath().split('|'); @@ -93,12 +104,15 @@ QStringList Command::menuPath(QAction* action) const // add it back to the path path << lastPart; + // cache the path + settings.setValue("scripts/" + scriptFileName + "/menu", path); + if (priority != 0) { action->setProperty("menu priority", priority); } // try to translate each part of the path - // not ideal, but menus should already be in the translation file + // not ideal, but most menus should already be in the translation file QStringList translatedPath; foreach (QString part, path) translatedPath << tr(part.toUtf8()); From 6f2dfecaf8d36ac77b391ea08ecfeb063c059347 Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Sat, 30 Nov 2024 08:53:02 -0500 Subject: [PATCH 3/3] Use a deferred start to initialize force fields Significantly speeds startup on my Mac Signed-off-by: Geoff Hutchison --- avogadro/qtplugins/forcefield/CMakeLists.txt | 5 ++- avogadro/qtplugins/forcefield/forcefield.cpp | 44 ++++++++++++-------- avogadro/qtplugins/forcefield/forcefield.h | 2 + 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/avogadro/qtplugins/forcefield/CMakeLists.txt b/avogadro/qtplugins/forcefield/CMakeLists.txt index cbd6fb14a6..484689aede 100644 --- a/avogadro/qtplugins/forcefield/CMakeLists.txt +++ b/avogadro/qtplugins/forcefield/CMakeLists.txt @@ -61,5 +61,6 @@ if (NOT BUILD_GPL_PLUGINS) ) endif() -install(PROGRAMS ${forcefields} -DESTINATION "${INSTALL_LIBRARY_DIR}/avogadro2/scripts/energy/") +# Don't install the scripts - we'll use these as plugins +# install(PROGRAMS ${forcefields} +# DESTINATION "${INSTALL_LIBRARY_DIR}/avogadro2/scripts/energy/") diff --git a/avogadro/qtplugins/forcefield/forcefield.cpp b/avogadro/qtplugins/forcefield/forcefield.cpp index e78095fbe2..70bf78172f 100644 --- a/avogadro/qtplugins/forcefield/forcefield.cpp +++ b/avogadro/qtplugins/forcefield/forcefield.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -65,24 +66,6 @@ Forcefield::Forcefield(QObject* parent_) m_gradientTolerance = settings.value("gradientTolerance", 1.0e-4).toDouble(); settings.endGroup(); - // prefer to use Python interface scripts if available - refreshScripts(); - - // add the openbabel calculators in case they don't exist -#ifdef BUILD_GPL_PLUGINS - // These directly use Open Babel and are fast - qDebug() << " registering GPL plugins"; - Calc::EnergyManager::registerModel(new OBEnergy("MMFF94")); - Calc::EnergyManager::registerModel(new OBEnergy("UFF")); - Calc::EnergyManager::registerModel(new OBEnergy("GAFF")); -#else - // These call obmm and can be slower - qDebug() << " registering obmm plugins"; - Calc::EnergyManager::registerModel(new OBMMEnergy("MMFF94")); - Calc::EnergyManager::registerModel(new OBMMEnergy("UFF")); - Calc::EnergyManager::registerModel(new OBMMEnergy("GAFF")); -#endif - QAction* action = new QAction(this); action->setEnabled(true); action->setText(tr("Optimize Geometry")); @@ -133,6 +116,9 @@ Forcefield::Forcefield(QObject* parent_) action->setData(unfreezeAction); connect(action, SIGNAL(triggered()), SLOT(unfreezeSelected())); m_actions.push_back(action); + + // single-shot timer to allow the GUI to start up + QTimer::singleShot(500, this, SLOT(deferredStart())); } Forcefield::~Forcefield() {} @@ -142,6 +128,28 @@ QList Forcefield::actions() const return m_actions; } +void Forcefield::deferredStart() +{ + + // prefer to use Python interface scripts if available + refreshScripts(); + + // add the openbabel calculators in case they don't exist +#ifdef BUILD_GPL_PLUGINS + // These directly use Open Babel and are fast + qDebug() << " registering GPL plugins"; + Calc::EnergyManager::registerModel(new OBEnergy("MMFF94")); + Calc::EnergyManager::registerModel(new OBEnergy("UFF")); + Calc::EnergyManager::registerModel(new OBEnergy("GAFF")); +#else + // These call obmm and can be slower + qDebug() << " registering obmm plugins"; + Calc::EnergyManager::registerModel(new OBMMEnergy("MMFF94")); + Calc::EnergyManager::registerModel(new OBMMEnergy("UFF")); + Calc::EnergyManager::registerModel(new OBMMEnergy("GAFF")); +#endif +} + QStringList Forcefield::menuPath(QAction* action) const { QStringList path; diff --git a/avogadro/qtplugins/forcefield/forcefield.h b/avogadro/qtplugins/forcefield/forcefield.h index a08e75675c..7b8bcc98f9 100644 --- a/avogadro/qtplugins/forcefield/forcefield.h +++ b/avogadro/qtplugins/forcefield/forcefield.h @@ -76,6 +76,8 @@ private slots: void freezeSelected(); void unfreezeSelected(); + void deferredStart(); + private: QList m_actions; QtGui::Molecule* m_molecule = nullptr;