Skip to content

Commit

Permalink
Refs #7614 - Group interfaces into submenus by category.
Browse files Browse the repository at this point in the history
UserSubWindow interfaces will no longer compile unless they define a
static categoriesInfo() function.  PyQt interfaces define their categories
in the Mantid properties file.  If the categories string provided by an
interface (UserSubWindow or PyQt) is empty, then the interface will be
put in an "Uncategorised" submenu.

Multiple categories per interface are allowed, but nested categories have
not been implemented.

We map interface names to categories when setting up.  Removed the old
pyqt_interfaces list and replaced it with calls to the keys() method of
the map instead.
  • Loading branch information
PeterParker committed Oct 22, 2013
1 parent 55e2de1 commit 757eb65
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 22 deletions.
4 changes: 2 additions & 2 deletions Code/Mantid/Framework/Properties/Mantid.properties.template
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ default.facility = ISIS
# Set a default instrument
default.instrument =

# Set of PyQt interfaces to add to the Interfaces menu, separated by a space
mantidqt.python_interfaces = DGS_Reduction.py ORNL_SANS.py REFL_Reduction.py REFL_SF_Calculator.py REFM_Reduction.py TofConverter.py ISIS_Reflectometry.py Powder_Diffraction_Reduction.py
# Set of PyQt interfaces to add to the Interfaces menu, separated by a space. Interfaces are seperated from their respective categories by a "/".
mantidqt.python_interfaces = Direct/DGS_Reduction.py SANS/ORNL_SANS.py Reflectometry/REFL_Reduction.py Reflectometry/REFL_SF_Calculator.py Reflectometry/REFM_Reduction.py Utility/TofConverter.py Reflectometry/ISIS_Reflectometry.py Diffraction/Powder_Diffraction_Reduction.py
mantidqt.python_interfaces_directory = @MANTID_ROOT@/scripts

# Where to find mantid plugin libraries
Expand Down
102 changes: 84 additions & 18 deletions Code/Mantid/MantidPlot/src/ApplicationWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@

#include <stdio.h>
#include <stdlib.h>
#include <cassert>

#include <qwt_scale_engine.h>
#include <QFileDialog>
Expand Down Expand Up @@ -168,11 +169,13 @@
#include <QSignalMapper>
#include <QDesktopWidget>
#include <QPair>
#include <QtAlgorithms>
#include <zlib.h>

#include <gsl/gsl_sort.h>

#include <boost/regex.hpp>
#include <boost/scoped_ptr.hpp>

//Mantid
#include "ScriptingWindow.h"
Expand Down Expand Up @@ -451,9 +454,37 @@ void ApplicationWindow::init(bool factorySettings, const QStringList& args)
scriptingWindow = NULL;
d_text_editor = NULL;

// List of registered PyQt interfaces
QString pyqt_interfaces_as_str = QString::fromStdString(Mantid::Kernel::ConfigService::Instance().getString("mantidqt.python_interfaces"));
pyqt_interfaces = QStringList::split(" ", pyqt_interfaces_as_str);
// Parse the list of registered PyQt interfaces and their respective categories.
QString pyQtInterfacesProperty = QString::fromStdString(Mantid::Kernel::ConfigService::Instance().getString("mantidqt.python_interfaces"));
foreach(const QString pyQtInterfaceInfo, QStringList::split(" ", pyQtInterfacesProperty))
{
QString interfaceName;
QSet<QString> interfaceCategories;
const QStringList tokens = QStringList::split("/", pyQtInterfaceInfo);

if( tokens.size() == 0 ) // Empty token - ignore.
{
continue;
}
else if( tokens.size() == 1 ) // Assume missing category.
{
interfaceCategories += "Uncatagorised";
interfaceName = tokens[0];
}
else if( tokens.size() == 2 ) // Assume correct interface name and categories.
{
interfaceCategories += QStringList::split(";", tokens[0]).toSet();
interfaceName = tokens[1];
}
else // Too many forward slashes, or no space between two interfaces. Warn user and move on.
{
g_log.warning() << "The mantidqt.python_interfaces property contains an unparsable value: "
<< pyQtInterfaceInfo.toStdString();
continue;
}

m_pyQtInterfaceToCategoriesMap[interfaceName] = interfaceCategories;
}

renamedTables = QStringList();
if (!factorySettings)
Expand Down Expand Up @@ -1514,37 +1545,53 @@ void ApplicationWindow::customMenu(MdiSubWindow* w)
}

myMenuBar()->insertItem(tr("&Catalog"),icat);

// -- INTERFACE MENU --

interfaceMenu = new QMenu(this);
interfaceMenu->setObjectName("interfaceMenu");
myMenuBar()->insertItem(tr("&Interfaces"), interfaceMenu);
m_interfaceActions.clear();

const MantidQt::API::InterfaceManager interfaceManager;
MantidQt::API::InterfaceManager interfaceManager;
const QString scriptsDir = QString::fromStdString(Mantid::Kernel::ConfigService::Instance().getString("mantidqt.python_interfaces_directory"));

// A collection of the names of each interface as they appear in the menu and also "data"
// relating to how each interface can be opened. The data can either be a python file
// location, or else just the name of the interface as known to the InterfaceManager.
// relating to how each interface can be opened. Elsewhere, the data is expected to be a
// python file name, or else just the name of the interface as known to the InterfaceManager.
QList<QPair<QString, QString>> interfaceNameDataPairs;

// Keeping track of all unique categories.
QSet<QString> allCategories;

// Map interfaces to their categories.
QMap<QString, QSet<QString>> interfaceCategories;

// Add all interfaces inherited from UserSubWindow to the collection.
foreach(const QString userSubWindowName, interfaceManager.getUserSubWindowKeys())
{
interfaceNameDataPairs.append(qMakePair(userSubWindowName, userSubWindowName));

const QSet<QString> categories = UserSubWindowFactory::Instance().getInterfaceCategories(userSubWindowName);

interfaceCategories[userSubWindowName] = categories;
allCategories += categories;
}

// Add all PyQt interfaces to the collection.
foreach(const QString pyQtInterface, pyqt_interfaces)
foreach(const QString pyQtInterfaceFile, m_pyQtInterfaceToCategoriesMap.keys())
{
const QString scriptPath = scriptsDir + '/' + pyQtInterface;
const QString scriptPath = scriptsDir + '/' + pyQtInterfaceFile;

if( QFileInfo(scriptPath).exists() )
{
QString pyQtInterfaceName = QFileInfo(scriptPath).baseName().replace("_", " ");
const QString pyQtInterfaceName = QFileInfo(scriptPath).baseName().replace("_", " ");
interfaceNameDataPairs.append(qMakePair(pyQtInterfaceName, scriptPath));

// Keep track of the interface's categories as we go.
auto categories = m_pyQtInterfaceToCategoriesMap[pyQtInterfaceFile];
interfaceCategories[pyQtInterfaceName] = categories;
allCategories += categories;
}
else
{
Expand All @@ -1553,16 +1600,34 @@ void ApplicationWindow::customMenu(MdiSubWindow* w)
}
}

// Turn the name/data pairs into QActions with which we populate the menu.
// Create a submenu for each category. Make sure submenus are in alphabetical order.
QMap<QString, QMenu *> categoryMenus;
auto sortedCategories = allCategories.toList();
qSort(sortedCategories);
foreach(const QString category, sortedCategories)
{
QMenu * categoryMenu = new QMenu(interfaceMenu);
categoryMenu->setObjectName(category + "Menu");
interfaceMenu->insertItem(tr(category), categoryMenu);
categoryMenus[category] = categoryMenu;
}

// Turn the name/data pairs into QActions with which we populate the menus.
foreach(const auto interfaceNameDataPair, interfaceNameDataPairs)
{
const QString name = interfaceNameDataPair.first;
const QString data = interfaceNameDataPair.second;

QAction * openInterface = new QAction(tr(name), interfaceMenu);
openInterface->setData(data);
interfaceMenu->addAction(openInterface);
m_interfaceActions.append(openInterface);

foreach(const QString category, interfaceCategories[name])
{
QAction * openInterface = new QAction(tr(name), interfaceMenu);
openInterface->setData(data);
assert(categoryMenus.contains(category));
categoryMenus[category]->addAction(openInterface);

// Update separate list containing all interface actions.
m_interfaceActions.append(openInterface);
}
}

connect(interfaceMenu, SIGNAL(triggered(QAction*)), this, SLOT(performCustomAction(QAction*)));
Expand Down Expand Up @@ -5304,11 +5369,12 @@ void ApplicationWindow::readSettings()
{
QFileInfo fi(settings.value(keyName).toString());
QString baseName = fi.fileName();
if (pyqt_interfaces.contains(baseName))
const QStringList pyQtInferfaces = m_pyQtInterfaceToCategoriesMap.keys();
if (pyQtInferfaces.contains(baseName))
continue;

if ( menu.contains("Interfaces")==0 &&
(user_windows.grep(keyName).size() > 0 || pyqt_interfaces.grep(keyName).size() > 0) )
(user_windows.grep(keyName).size() > 0 || pyQtInferfaces.grep(keyName).size() > 0) )
{
duplicated_custom_menu.append(menu+"/"+keyName);
}
Expand Down
4 changes: 2 additions & 2 deletions Code/Mantid/MantidPlot/src/ApplicationWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -1329,8 +1329,8 @@ public slots:
QStringList renamedTables;
// List of removed interfaces
QStringList removed_interfaces;
// List of PyQt interfaces to be added to the Interfaces menu
QStringList pyqt_interfaces;
// Map PyQt interfaces that are to be added to the Interfaces menu to their respective categories.
QMap<QString, QSet<QString>> m_pyQtInterfaceToCategoriesMap;

//! \name variables used when user copy/paste markers
//@{
Expand Down
13 changes: 13 additions & 0 deletions Code/Mantid/MantidQt/API/inc/MantidQtAPI/InterfaceFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,22 @@ class EXPORT_OPT_MANTIDQT_API UserSubWindowFactoryImpl : public Mantid::Kernel::
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;
}

public:
// Override createUnwrapped to search through the alias list
UserSubWindow * createUnwrapped(const std::string & name) const;

QSet<QString> getInterfaceCategories(const QString & interfaceName) const;

protected:
// Unhide the inherited create method
Expand Down Expand Up @@ -151,6 +162,8 @@ class EXPORT_OPT_MANTIDQT_API UserSubWindowFactoryImpl : public Mantid::Kernel::
QHash<QString, std::string> m_aliasLookup;
/// An index of multiply defined aliases
QHash<QString, QList<std::string> > m_badAliases;
/// A map of interfaces to their categories.
QHash<QString, QSet<QString>> m_categoryLookup;
Mantid::Kernel::Logger & g_log;
};

Expand Down
15 changes: 15 additions & 0 deletions Code/Mantid/MantidQt/API/src/InterfaceFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ UserSubWindow * UserSubWindowFactoryImpl::createUnwrapped(const std::string & na
return window;
}

/**
* Return the set of categories that the interface with the given name belongs to.
*
* @param name :: The name of the interface.
* @returns the set of category names if an interface with the given name has been registered,
* else an empty set.
*/
QSet<QString> UserSubWindowFactoryImpl::getInterfaceCategories(const QString & interfaceName) const
{
if( !m_categoryLookup.contains(interfaceName) )
return QSet<QString>();

return m_categoryLookup[interfaceName];
}

//----------------------------------------
// Public member functions
//----------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ namespace MantidQt
aliasList.insert("Homer");
return aliasList;
}
// This interface's categories.
static QString categoryInfo() { return "Indirect;Direct"; }

private slots:
void helpClicked();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace MantidQt
public:
/// The name of the interface as registered into the factory
static std::string name() { return "Create MD Workspace"; }
// This interface's categories.
static QString categoryInfo() { return "Indirect"; }
/// Default Constructor
CreateMDWorkspace(QWidget *parent = 0);
/// Destructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ namespace IDA
public:
/// The name of the interface as registered into the factory
static std::string name() { return "Indirect Data Analysis"; }
// This interface's categories.
static QString categoryInfo() { return "Indirect"; }
/// Default Constructor
IndirectDataAnalysis(QWidget *parent = 0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class IndirectDiffractionReduction : public MantidQt::API::UserSubWindow
public:
/// The name of the interface as registered into the factory
static std::string name() { return "Indirect Diffraction"; }
// This interface's categories.
static QString categoryInfo() { return "Indirect"; }

public:
/// Default Constructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ namespace MantidQt
~IndirectLoadAscii();
/// Interface name
static std::string name() { return "Indirect Load Ascii"; }
// This interface's categories.
static QString categoryInfo() { return "Indirect"; }
virtual void initLayout();

private slots:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ class MantidEV : public API::UserSubWindow

/// The name of the interface as registered into the factory
static std::string name() { return "SCD Event Data Reduction"; }
// This interface's categories.
static QString categoryInfo() { return "Diffraction"; }

public slots:
/// Slot for Q-Point selection notification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class MuonAnalysis : public MantidQt::API::UserSubWindow
public:
/// Name of the interface
static std::string name() { return "Muon Analysis"; }
// This interface's categories.
static QString categoryInfo() { return "Muon"; }

/// Default Constructor
MuonAnalysis(QWidget *parent = 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class SANSRunWindow : public MantidQt::API::UserSubWindow
public:
/// Name of the interface
static std::string name() { return "SANS ISIS"; }
// This interface's categories.
static QString categoryInfo() { return "SANS"; }

///Stores the batch or single run mode selection
enum States {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class StepScan : public API::UserSubWindow
public:
/// The name of the interface as registered into the factory
static std::string name() { return "Step Scan Analysis"; }
// This interface's categories.
static QString categoryInfo() { return "General"; }

StepScan(QWidget *parent = 0);
~StepScan();
Expand Down

0 comments on commit 757eb65

Please sign in to comment.