Mantid
Loading...
Searching...
No Matches
AlgorithmSelectorWidget.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
10
11#include "boost/algorithm/string.hpp"
12
13#include <QApplication>
14#include <QCompleter>
15#include <QDrag>
16#include <QHBoxLayout>
17#include <QMimeData>
18#include <QMouseEvent>
19#include <QPushButton>
20
21using namespace Mantid::Kernel;
22using namespace Mantid::API;
23
25
26//----------------------------------------------------------------------------------------------
30 : QWidget(parent), m_tree(nullptr), m_findAlg(nullptr), m_execButton(nullptr),
31 m_updateObserver(*this, &AlgorithmSelectorWidget::handleAlgorithmFactoryUpdate), m_updateInProgress(false) {
32 auto *buttonLayout = new QHBoxLayout();
33
34 m_tree = new AlgorithmTreeWidget(this);
35 m_tree->setHeaderLabel("Algorithms");
36 connect(m_tree, SIGNAL(itemSelectionChanged()), this, SLOT(treeSelectionChanged()));
37 connect(m_tree, SIGNAL(executeAlgorithm(const QString &, int)), this, SLOT(executeSelected()));
38
40 m_findAlg->setEditable(true);
41 m_findAlg->completer()->setCompletionMode(QCompleter::PopupCompletion);
42
43 // Make the algorithm drop down use all the sapce it can horizontally
44 QSizePolicy expandHoriz;
45 expandHoriz.setHorizontalPolicy(QSizePolicy::Expanding);
46 m_findAlg->setSizePolicy(expandHoriz);
47
48 connect(m_findAlg, SIGNAL(enterPressed()), this, SLOT(executeSelected()));
49 connect(m_findAlg, SIGNAL(editTextChanged(const QString &)), this, SLOT(findAlgTextChanged(const QString &)));
50
51 m_execButton = new QPushButton("Execute");
52 connect(m_execButton, SIGNAL(clicked()), this, SLOT(executeSelected()));
53 buttonLayout->addWidget(m_execButton);
54
55 buttonLayout->addWidget(m_findAlg);
56
57 // Layout the tree and combo box
58 QVBoxLayout *layout = new QVBoxLayout(this);
59 // this->setLayout(layout);
60 layout->addLayout(buttonLayout);
61 layout->addWidget(m_tree);
62
63 // The poco notification will be dispatched from the callers thread but we
64 // need to
65 // make sure the updates to the widgets happen on the GUI thread. Dispatching
66 // through a Qt signal will make sure it is in the correct thread.
67 AlgorithmFactory::Instance().notificationCenter.addObserver(m_updateObserver);
68 connect(this, SIGNAL(algorithmFactoryUpdateReceived()), this, SLOT(update()));
69}
70
71//----------------------------------------------------------------------------------------------
75 AlgorithmFactory::Instance().notificationCenter.removeObserver(m_updateObserver);
76}
77
79bool AlgorithmSelectorWidget::showExecuteButton() const { return m_execButton->isVisible(); }
80
82void AlgorithmSelectorWidget::showExecuteButton(const bool val) { m_execButton->setVisible(val); }
83
84//---------------------------------------------------------------------------
87 m_updateInProgress = true;
89 m_tree->update();
90 m_updateInProgress = false;
91}
92
93//---------------------------------------------------------------------------
97 auto alg = this->getSelectedAlgorithm();
98 if (!alg.name.isEmpty()) {
99 emit executeAlgorithm(alg.name, alg.version);
100 }
101}
102
103//---------------------------------------------------------------------------
106 int i = m_findAlg->findText(text, Qt::MatchFixedString);
107 if (i >= 0)
108 m_findAlg->setCurrentIndex(i);
109 // De-select from the tree
110 m_tree->blockSignals(true);
111 m_tree->setCurrentIndex(QModelIndex());
112 m_tree->blockSignals(false);
113
114 // Emit the signal
115 auto alg = this->getSelectedAlgorithm();
116 emit algorithmSelectionChanged(alg.name, alg.version);
117}
118
119//---------------------------------------------------------------------------
122 auto alg = this->getSelectedAlgorithm();
123 // Select in the combo box
124 m_findAlg->blockSignals(true);
125 m_findAlg->setCurrentIndex(m_findAlg->findText(alg.name, Qt::MatchFixedString));
126 m_findAlg->blockSignals(false);
127 // Emit the signal
128 emit algorithmSelectionChanged(alg.name, alg.version);
129}
130
131//---------------------------------------------------------------------------
137 if (alg.name.isEmpty())
139 return alg;
140}
141
142//---------------------------------------------------------------------------
149 m_findAlg->blockSignals(true);
150 m_findAlg->setCurrentIndex(m_findAlg->findText(algName, Qt::MatchFixedString));
151 m_findAlg->blockSignals(false);
152 // De-select from the tree
153 m_tree->blockSignals(true);
154 m_tree->setCurrentIndex(QModelIndex());
155 m_tree->blockSignals(false);
156}
157
165}
166
167//============================================================================
168//============================================================================
169//============================================================================
170// Use an anonymous namespace to keep these at file scope
171namespace {
172
173bool AlgorithmDescriptorLess(const AlgorithmDescriptor &d1, const AlgorithmDescriptor &d2) {
174 if (d1.category < d2.category)
175 return true;
176 else if (d1.category == d2.category && d1.name < d2.name)
177 return true;
178 else if (d1.category == d2.category && d1.name == d2.name && d1.version > d2.version)
179 return true;
180
181 return false;
182}
183
184bool AlgorithmDescriptorNameLess(const AlgorithmDescriptor &d1, const AlgorithmDescriptor &d2) {
185 return d1.name < d2.name;
186}
187} // namespace
188
189//============================================================================
190//======================= AlgorithmTreeWidget ================================
191//============================================================================
196 SelectedAlgorithm alg("", 0);
197
198 auto isCategoryName = [](const QTreeWidgetItem *item) {
199 return (item->childCount() != 0 && !item->text(0).contains(" v."));
200 };
201
202 QList<QTreeWidgetItem *> items = this->selectedItems();
203 if (items.size() > 0 && !isCategoryName(items[0])) {
204 QString str = items[0]->text(0);
205 QStringList lst = str.split(" v.");
206 alg.name = lst[0];
207 alg.version = lst[1].toInt();
208 }
209 return alg;
210}
211
212//---------------------------------------------------------------------------
215 if (e->button() == Qt::LeftButton) {
216 if (!itemAt(e->pos()))
217 selectionModel()->clear();
218 m_dragStartPosition = e->pos();
219 }
220
221 QTreeWidget::mousePressEvent(e);
222}
223
224//---------------------------------------------------------------------------
227 if (!(e->buttons() & Qt::LeftButton))
228 return;
229 if ((e->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance())
230 return;
231
232 // Start dragging
233 QDrag *drag = new QDrag(this);
234 auto *mimeData = new QMimeData;
235
236 mimeData->setText("Algorithm");
237 drag->setMimeData(mimeData);
238
239 Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
240 (void)dropAction;
241}
242
243//---------------------------------------------------------------------------
246 auto alg = this->getSelectedAlgorithm();
247 if (!alg.name.isEmpty()) {
248 // Emit the signal that we are executing
249 emit executeAlgorithm(alg.name, alg.version);
250 return;
251 }
252 QTreeWidget::mouseDoubleClickEvent(e);
253}
254
255//---------------------------------------------------------------------------
258 this->clear();
259
260 using AlgNamesType = std::vector<AlgorithmDescriptor>;
261 AlgNamesType names = AlgorithmFactory::Instance().getDescriptors(false, false);
262
263 // sort by category/name/version to fill QTreeWidget
264 sort(names.begin(), names.end(), AlgorithmDescriptorLess);
265
266 QMap<QString, QTreeWidgetItem *> categories; // keeps track of categories added to the tree
267 QMap<QString, QTreeWidgetItem *> algorithms; // keeps track of algorithms
268 // added to the tree (needed in
269 // case there are different
270 // versions of an algorithm)
271
272 for (AlgNamesType::const_iterator i = names.begin(); i != names.end(); ++i) {
273 QString algName = QString::fromStdString(i->name);
274 QString catName = QString::fromStdString(i->category);
275 QStringList subCats = catName.split('\\');
276 if (!categories.contains(catName)) {
277 if (subCats.size() == 1) {
278 QTreeWidgetItem *catItem = new QTreeWidgetItem(QStringList(catName));
279 categories.insert(catName, catItem);
280 this->addTopLevelItem(catItem);
281 } else {
282 QString cn = subCats[0];
283 QTreeWidgetItem *catItem = nullptr;
284 int n = subCats.size();
285 for (int j = 0; j < n; j++) {
286 if (categories.contains(cn)) {
287 catItem = categories[cn];
288 } else {
289 QTreeWidgetItem *newCatItem = new QTreeWidgetItem(QStringList(subCats[j]));
290 categories.insert(cn, newCatItem);
291 if (!catItem) {
292 this->addTopLevelItem(newCatItem);
293 } else {
294 catItem->addChild(newCatItem);
295 }
296 catItem = newCatItem;
297 }
298 if (j != n - 1)
299 cn += "\\" + subCats[j + 1];
300 }
301 }
302 }
303
304 QTreeWidgetItem *algItem = new QTreeWidgetItem(QStringList(algName + " v." + QString::number(i->version)));
305 QString cat_algName = catName + algName;
306 if (!algorithms.contains(cat_algName)) {
307 algorithms.insert(cat_algName, algItem);
308 categories[catName]->addChild(algItem);
309 } else
310 algorithms[cat_algName]->addChild(algItem);
311 }
312}
313
314//============================================================================
315//============================== FindAlgComboBox =============================
316//============================================================================
320 if (e->key() == Qt::Key_Return) {
321 emit enterPressed();
322 return;
323 }
324 QComboBox::keyPressEvent(e);
325}
326
327//---------------------------------------------------------------------------
330 // include hidden categories in the combo list box
331 AlgNamesType names = AlgorithmFactory::Instance().getDescriptors(true, false);
332 addAliases(names);
333
334 // sort by algorithm names only to fill this combobox
335 sort(names.begin(), names.end(), AlgorithmDescriptorNameLess);
336
337 this->clear();
338 std::string prevName = "";
339 for (AlgNamesType::const_iterator i = names.begin(); i != names.end(); ++i) {
340 if (i->name != prevName)
341 this->addItem(QString::fromStdString(i->name));
342 prevName = i->name;
343 }
344 this->setCurrentIndex(-1);
345}
346
349 AlgNamesType aliasList;
350 for (AlgNamesType::const_iterator i = algNamesList.begin(); i != algNamesList.end(); ++i) {
351 // the alias is not empty and is not just different by case from the name
352 if ((!i->alias.empty()) && (!boost::iequals(i->alias, i->name))) {
353 AlgorithmDescriptor newAlias(*i);
354 newAlias.name = i->alias + " [" + i->name + "]";
355 aliasList.emplace_back(newAlias);
356 }
357 }
358 // add them to the list - unsorted
359 algNamesList.reserve(algNamesList.size() + aliasList.size());
360 algNamesList.insert(algNamesList.end(), aliasList.begin(), aliasList.end());
361}
362
364QString FindAlgComboBox::stripAlias(const QString &text) const {
365 QString retVal = text;
366 int foundOpen = text.indexOf("[");
367 if (foundOpen != -1) {
368 int foundClose = text.lastIndexOf("]");
369 if (foundClose != -1)
370 retVal = text.mid(foundOpen + 1, foundClose - foundOpen - 1);
371 }
372 return retVal;
373}
374
375//---------------------------------------------------------------------------
378 // typed selection
379 QString typedText = this->currentText().trimmed(); // text as typed in the combobox
380 if (!typedText.isEmpty()) // if the text is not empty
381 {
382 // find the closest matching entry
383 int matchedIndex = this->findText(typedText, Qt::MatchStartsWith);
384 if (matchedIndex > -1) {
385 typedText = this->itemText(matchedIndex); // text in the combobox at the matched index
386 typedText = stripAlias(typedText);
387 }
388 }
389 return SelectedAlgorithm(typedText, -1);
390}
391
392} // namespace MantidQt::MantidWidgets
void addItem(WorkspaceGroup &self, const std::string &name)
A widget consisting of a ComboBox and a TreeWidget to allow a user to select an algorithm either by c...
void handleAlgorithmFactoryUpdate(Mantid::API::AlgorithmFactoryUpdateNotification_ptr)
Callback for AlgorithmFactory update notifications.
Poco::NObserver< AlgorithmSelectorWidget, Mantid::API::AlgorithmFactoryUpdateNotification > m_updateObserver
Observes algorithm factory update notifications.
SelectedAlgorithm getSelectedAlgorithm()
Return the selected algorithm.
void setSelectedAlgorithm(QString &algName)
Set which algorithm is currently selected.
void findAlgTextChanged(const QString &text)
Show the selection in the tree when it changes in the combo.
void executeSelected()
Slot called to execute whatever is the selected algorithm.
void treeSelectionChanged()
Show the selection in the combo when it changes in the tree.
void algorithmSelectionChanged(const QString &, int)
Tree widget with the categories and algorithms listed.
void executeAlgorithm(const QString &, int)
Signal emitted when the widget requests that we execute that algorithm.
void mousePressEvent(QMouseEvent *e) override
SLOT called when clicking the mouse around the tree.
void mouseDoubleClickEvent(QMouseEvent *e) override
SLOT called when double-clicking on an entry in the tree.
void mouseMoveEvent(QMouseEvent *e) override
SLOT called when dragging the mouse around the tree.
SelectedAlgorithm getSelectedAlgorithm()
Return the selected algorithm in the tree.
void update()
Update the list of algos in the tree.
void update()
Update the list of algos in the combo box.
void keyPressEvent(QKeyEvent *e) override
Called when the combo box for finding algorithms has a key press event
void addAliases(AlgNamesType &algNamesList)
Adds alias entries to the list of algorithms.
std::vector< Mantid::API::AlgorithmDescriptor > AlgNamesType
QString stripAlias(const QString &text) const
if a string is for an alias convert it to the algorithm name
SelectedAlgorithm getSelectedAlgorithm()
Return the selected algorithm.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
const Poco::AutoPtr< Mantid::Kernel::DynamicFactory< Algorithm >::UpdateNotification > & AlgorithmFactoryUpdateNotification_ptr
Represents the algorithm selected by the user Contains name and version.
Structure uniquely describing an algorithm with its name, category and version.
std::string name
Algorithm Name.