diff --git a/MantidPlot/src/ApplicationWindow.cpp b/MantidPlot/src/ApplicationWindow.cpp index 28e05f98eb294afc56cc3c0e5c0736e33154e82f..706d97bae072cd802b59930e49c7b6a28d811b65 100644 --- a/MantidPlot/src/ApplicationWindow.cpp +++ b/MantidPlot/src/ApplicationWindow.cpp @@ -508,8 +508,7 @@ void ApplicationWindow::init(bool factorySettings, const QStringList &args) { qMakePair(userSubWindowName, userSubWindowName)); const QSet<QString> categories = - UserSubWindowFactory::Instance().getInterfaceCategories( - userSubWindowName); + UserSubWindowFactory::Instance().categories(userSubWindowName); m_interfaceCategories[userSubWindowName] = categories; m_allCategories += categories; diff --git a/qt/applications/workbench/workbench/app/mainwindow.py b/qt/applications/workbench/workbench/app/mainwindow.py index 7c7aedc2e3554ec07a3528da9cb657dacc4ea4db..abf4d98d0b6d3056b8ae34d7f249ed3f4a985692 100644 --- a/qt/applications/workbench/workbench/app/mainwindow.py +++ b/qt/applications/workbench/workbench/app/mainwindow.py @@ -52,6 +52,8 @@ from mantidqt.widgets.codeeditor.execution import PythonCodeExecution # noqa from mantidqt.utils.qt import (add_actions, create_action, plugins, widget_updates_disabled) # noqa from mantidqt.project.project import Project # noqa +from mantidqt.interfacemanager import InterfaceManager # noqa +from mantidqt.usersubwindowfactory import UserSubWindowFactory # noqa # Pre-application setup plugins.setup_library_paths() @@ -175,8 +177,9 @@ class MainWindow(QMainWindow): self.project = None self.project_recovery = None - # Interface Runner - self.executioner = None + # Interfaces + self.interface_manager = None + self.interface_executor = None def setup(self): # menus must be done first so they can be filled by the @@ -226,6 +229,9 @@ class MainWindow(QMainWindow): self.project_recovery = ProjectRecovery(globalfiguremanager=GlobalFigureManager, multifileinterpreter=self.editor.editors, main_window=self) + + self.interface_executor = PythonCodeExecution() + self.interface_executor.sig_exec_error.connect(lambda errobj: logger.warning(str(errobj))) self.interface_manager = InterfaceManager() # uses default configuration as necessary @@ -234,13 +240,12 @@ class MainWindow(QMainWindow): self.setup_layout() self.create_actions() - self.populate_menus() def post_mantid_init(self): """Run any setup that requires mantid to have been initialized """ - self.populate_interfaces_menu() + self.populate_menus() self.algorithm_selector.refresh() # turn on algorithm factory notifications @@ -336,18 +341,41 @@ class MainWindow(QMainWindow): add_actions(self.file_menu, self.file_menu_actions) add_actions(self.view_menu, self.view_menu_actions) add_actions(self.help_menu, self.help_menu_actions) + self.populate_interfaces_menu() - def launch_custom_gui(self, filename): - if self.executioner is None: - self.executioner = PythonCodeExecution() - self.executioner.sig_exec_error.connect(lambda errobj: logger.warning(str(errobj))) - + def launch_custom_python_gui(self, filename): self.executioner.execute(open(filename).read(), filename) + def launch_custom_cpp_gui(self, interface_name): + interface = self.interface_manager.createSubWindow(interface_name) + interface.setAttribute(Qt.WA_DeleteOnClose, True) + interface.show() + def populate_interfaces_menu(self): + """Populate then Interfaces menu with all Python and C++ interfaces""" interface_dir = ConfigService['mantidqt.python_interfaces_directory'] - items = ConfigService['mantidqt.python_interfaces'].split() + interfaces = self._discover_python_interfaces(interface_dir) + interfaces.update(self._discover_cpp_interfaces()) + keys = list(interfaces.keys()) + keys.sort() + for key in keys: + submenu = self.interfaces_menu.addMenu(key) + names = interfaces[key] + names.sort() + for name in names: + if '.py' in name: + action = submenu.addAction(name.replace('.py', '').replace('_', ' ')) + script = os.path.join(interface_dir, name) + action.triggered.connect(lambda checked_py, script=script: self.launch_custom_python_gui(script)) + else: + action = submenu.addAction(name) + action.triggered.connect(lambda checked_cpp, name=name: + self.launch_custom_cpp_gui(name)) + + def _discover_python_interfaces(self, interface_dir): + """Return a dictionary mapping a category to a set of named Python interfaces""" + items = ConfigService['mantidqt.python_interfaces'].split() # list of custom interfaces that are not qt4/qt5 compatible GUI_BLACKLIST = ['ISIS_Reflectometry_Old.py', 'Frequency_Domain_Analysis_Old.py', @@ -364,21 +392,23 @@ class MainWindow(QMainWindow): if scriptname in GUI_BLACKLIST: logger.information('Not adding gui "{}"'.format(scriptname)) continue - temp = interfaces.get(key, []) - temp.append(scriptname) - interfaces[key] = temp + interfaces.setdefault(key, []).append(scriptname) - # add the interfaces to the menu - keys = list(interfaces.keys()) - keys.sort() - for key in keys: - submenu = self.interfaces_menu.addMenu(key) - names = interfaces[key] - names.sort() - for name in names: - action = submenu.addAction(name.replace('.py', '').replace('_', ' ')) - script = os.path.join(interface_dir, name) - action.triggered.connect(lambda checked, script=script: self.launch_custom_gui(script)) + return interfaces + + def _discover_cpp_interfaces(self): + """Return a dictionary mapping a category to a set of named C++ interfaces""" + interfaces = {} + cpp_interface_factory = UserSubWindowFactory.Instance() + interface_names = cpp_interface_factory.keys() + for name in interface_names: + categories = cpp_interface_factory.categories(name) + if len(categories) == 0: + categories = ["General"] + for category in categories: + interfaces.setdefault(category, []).append(name) + + return interfaces def add_dockwidget(self, plugin): """Create a dockwidget around a plugin and add the dock to window""" diff --git a/qt/python/mantidqt/_common.sip b/qt/python/mantidqt/_common.sip index dfafd05aa8ab3ebfc701c1c5158590fd39a79564..049b1c0327ce9e79776a0046039f2945bed62fe2 100644 --- a/qt/python/mantidqt/_common.sip +++ b/qt/python/mantidqt/_common.sip @@ -231,12 +231,28 @@ class UserSubWindow : QMainWindow { #include "MantidQtWidgets/Common/UserSubWindow.h" using namespace MantidQt::API; %End -public: +private: UserSubWindow(QWidget *parent = nullptr); - void setInterfaceName(const QString &iface_name); - void initializeLayout(); -protected: - virtual void initLayout() = 0; +}; + +class UserSubWindowFactoryImpl /PyName=UserSubWindowFactory/ { +%TypeHeaderCode +#include "MantidQtWidgets/Common/UserSubWindowFactory.h" +%End +public: + static SIP_PYOBJECT Instance() const; + %MethodCode + auto &cppInstance = MantidQt::API::UserSubWindowFactory::Instance(); + return sipConvertFromType(&cppInstance, sipType_UserSubWindowFactoryImpl, nullptr); + %End + UserSubWindow *createUnwrapped(const std::string &name) const; + QSet<QString> categories(const QString &interfaceName) const; + QStringList keys() const; + +private: + UserSubWindowFactoryImpl(); + UserSubWindowFactoryImpl(const UserSubWindowFactoryImpl &); + ~UserSubWindowFactoryImpl(); }; diff --git a/qt/python/mantidqt/usersubwindowfactory.py b/qt/python/mantidqt/usersubwindowfactory.py new file mode 100644 index 0000000000000000000000000000000000000000..ae6ac54b8fa271573295379933bfc1dc5d843f3c --- /dev/null +++ b/qt/python/mantidqt/usersubwindowfactory.py @@ -0,0 +1,15 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2019 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantidqt package +# +# +from __future__ import (absolute_import) + +from mantidqt.utils.qt import import_qt + + +UserSubWindowFactory = import_qt('._common', 'mantidqt', 'UserSubWindowFactory') diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/UserSubWindowFactory.h b/qt/widgets/common/inc/MantidQtWidgets/Common/UserSubWindowFactory.h index 0806051e2be850f763b9e6af7e848bb0b3bf02a9..ef56205dfa14911a64446a913669745bde641b85 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/UserSubWindowFactory.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/UserSubWindowFactory.h @@ -45,9 +45,8 @@ public: // Override createUnwrapped to search through the alias list UserSubWindow *createUnwrapped(const std::string &name) const override; - QSet<QString> getInterfaceCategories(const QString &interfaceName) const; - - QStringList getUserSubWindowKeys() const; + QSet<QString> categories(const QString &interfaceName) const; + QStringList keys() const; template <typename TYPE> void subscribe(); @@ -77,9 +76,51 @@ private: QHash<QString, QSet<QString>> m_categoryLookup; }; +template <typename TYPE> void UserSubWindowFactoryImpl::subscribe() { + std::string realName = TYPE::name(); + Mantid::Kernel::DynamicFactory<UserSubWindow>::subscribe<TYPE>(realName); + saveAliasNames<TYPE>(realName); + + // Make a record of each interface's categories. + const QStringList categories = + TYPE::categoryInfo().split(";", QString::SkipEmptyParts); + QSet<QString> result; + foreach (const QString category, categories) { + result.insert(category.trimmed()); + } + m_categoryLookup[QString::fromStdString(realName)] = result; +} + +/** + * Save the alias names of an interface + * @param realName :: The real name of the interface + */ +template <typename TYPE> +void UserSubWindowFactoryImpl::saveAliasNames(const std::string &realName) { + std::set<std::string> aliases = TYPE::aliases(); + for (const auto &alias_std_str : aliases) { + QString alias = QString::fromStdString(alias_std_str); + if (m_aliasLookup.contains(alias)) { + if (m_badAliases.contains(alias)) { + QList<std::string> names = m_badAliases.value(alias); + names.append(realName); + m_badAliases[alias] = names; + } else { + QList<std::string> names; + names.append(m_aliasLookup.value(alias)); + names.append(realName); + m_badAliases.insert(alias, names); + } + continue; + } + m_aliasLookup.insert(alias, realName); + } +} + /// The specific instantiation of the templated type using UserSubWindowFactory = Mantid::Kernel::SingletonHolder<UserSubWindowFactoryImpl>; + } // namespace API } // namespace MantidQt diff --git a/qt/widgets/common/src/InterfaceManager.cpp b/qt/widgets/common/src/InterfaceManager.cpp index 1ded3ba974d032988bc3e0e6d9fe728eee3ba8b4..494e3cc53bfb0e75fabbca10f4707b566bcda960 100644 --- a/qt/widgets/common/src/InterfaceManager.cpp +++ b/qt/widgets/common/src/InterfaceManager.cpp @@ -176,7 +176,7 @@ UserSubWindow *InterfaceManager::createSubWindow(const QString &interface_name, * refer to UserSubWindow classes */ QStringList InterfaceManager::getUserSubWindowKeys() const { - return UserSubWindowFactory::Instance().getUserSubWindowKeys(); + return UserSubWindowFactory::Instance().keys(); } //---------------------------------- diff --git a/qt/widgets/common/src/UserSubWindowFactory.cpp b/qt/widgets/common/src/UserSubWindowFactory.cpp index ee29cd9db8a27ac213feef1f75797cd60e5e5a98..da47021dcdaf149e2cccbeb4f5b02299dce0e1ff 100644 --- a/qt/widgets/common/src/UserSubWindowFactory.cpp +++ b/qt/widgets/common/src/UserSubWindowFactory.cpp @@ -68,8 +68,8 @@ UserSubWindowFactoryImpl::createUnwrapped(const std::string &name) const { *been registered, * else an empty set. */ -QSet<QString> UserSubWindowFactoryImpl::getInterfaceCategories( - const QString &interfaceName) const { +QSet<QString> +UserSubWindowFactoryImpl::categories(const QString &interfaceName) const { if (!m_categoryLookup.contains(interfaceName)) return QSet<QString>(); @@ -114,53 +114,12 @@ UserSubWindowFactoryImpl::createFromAlias(const std::string &name) const { } } -template <typename TYPE> void UserSubWindowFactoryImpl::subscribe() { - std::string realName = TYPE::name(); - Mantid::Kernel::DynamicFactory<UserSubWindow>::subscribe<TYPE>(realName); - saveAliasNames<TYPE>(realName); - - // Make a record of each interface's categories. - const QStringList categories = - TYPE::categoryInfo().split(";", QString::SkipEmptyParts); - QSet<QString> result; - foreach (const QString category, categories) { - result.insert(category.trimmed()); - } - m_categoryLookup[QString::fromStdString(realName)] = result; -} - -/** - * Save the alias names of an interface - * @param realName :: The real name of the interface - */ -template <typename TYPE> -void UserSubWindowFactoryImpl::saveAliasNames(const std::string &realName) { - std::set<std::string> aliases = TYPE::aliases(); - for (const auto &alias_std_str : aliases) { - QString alias = QString::fromStdString(alias_std_str); - if (m_aliasLookup.contains(alias)) { - if (m_badAliases.contains(alias)) { - QList<std::string> names = m_badAliases.value(alias); - names.append(realName); - m_badAliases[alias] = names; - } else { - QList<std::string> names; - names.append(m_aliasLookup.value(alias)); - names.append(realName); - m_badAliases.insert(alias, names); - } - continue; - } - m_aliasLookup.insert(alias, realName); - } -} - /** * The keys associated with UserSubWindow classes * @returns A QStringList containing the keys from the UserSubWindowFactory that * refer to UserSubWindow classes */ -QStringList UserSubWindowFactoryImpl::getUserSubWindowKeys() const { +QStringList UserSubWindowFactoryImpl::keys() const { QStringList key_list; const auto keys = getKeys(); for (const auto &key : keys) {