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 +
9
10#include "boost/algorithm/string.hpp"
11
12#include <QApplication>
13#include <QCompleter>
14#include <QDrag>
15#include <QHBoxLayout>
16#include <QMimeData>
17#include <QMouseEvent>
18#include <QPushButton>
19
20using namespace Mantid::Kernel;
21using namespace Mantid::API;
22
24
25//----------------------------------------------------------------------------------------------
29 : QWidget(parent), m_tree(nullptr), m_findAlg(nullptr), m_execButton(nullptr),
30 m_updateObserver(*this, &AlgorithmSelectorWidget::handleAlgorithmFactoryUpdate), m_updateInProgress(false) {
31 auto *buttonLayout = new QHBoxLayout();
32
33 m_tree = new AlgorithmTreeWidget(this);
34 m_tree->setHeaderLabel("Algorithms");
35 connect(m_tree, SIGNAL(itemSelectionChanged()), this, SLOT(treeSelectionChanged()));
36 connect(m_tree, SIGNAL(executeAlgorithm(const QString &, int)), this, SLOT(executeSelected()));
37
39 m_findAlg->setEditable(true);
40 m_findAlg->completer()->setCompletionMode(QCompleter::PopupCompletion);
41
42 // Make the algorithm drop down use all the sapce it can horizontally
43 QSizePolicy expandHoriz;
44 expandHoriz.setHorizontalPolicy(QSizePolicy::Expanding);
45 m_findAlg->setSizePolicy(expandHoriz);
46
47 connect(m_findAlg, SIGNAL(enterPressed()), this, SLOT(executeSelected()));
48 connect(m_findAlg, SIGNAL(editTextChanged(const QString &)), this, SLOT(findAlgTextChanged(const QString &)));
49
50 m_execButton = new QPushButton("Execute");
51 connect(m_execButton, SIGNAL(clicked()), this, SLOT(executeSelected()));
52 buttonLayout->addWidget(m_execButton);
53
54 buttonLayout->addWidget(m_findAlg);
55
56 // Layout the tree and combo box
57 QVBoxLayout *layout = new QVBoxLayout(this);
58 // this->setLayout(layout);
59 layout->addLayout(buttonLayout);
60 layout->addWidget(m_tree);
61
62 // The poco notification will be dispatched from the callers thread but we
63 // need to
64 // make sure the updates to the widgets happen on the GUI thread. Dispatching
65 // through a Qt signal will make sure it is in the correct thread.
66 AlgorithmFactory::Instance().notificationCenter.addObserver(m_updateObserver);
67 connect(this, SIGNAL(algorithmFactoryUpdateReceived()), this, SLOT(update()));
68}
69
70//----------------------------------------------------------------------------------------------
74 AlgorithmFactory::Instance().notificationCenter.removeObserver(m_updateObserver);
75}
76
78bool AlgorithmSelectorWidget::showExecuteButton() const { return m_execButton->isVisible(); }
79
81void AlgorithmSelectorWidget::showExecuteButton(const bool val) { m_execButton->setVisible(val); }
82
83//---------------------------------------------------------------------------
91
92//---------------------------------------------------------------------------
96 auto alg = this->getSelectedAlgorithm();
97 if (!alg.name.isEmpty()) {
98 emit executeAlgorithm(alg.name, alg.version);
99 }
100}
101
102//---------------------------------------------------------------------------
105 int i = m_findAlg->findText(text, Qt::MatchFixedString);
106 if (i >= 0)
107 m_findAlg->setCurrentIndex(i);
108 // De-select from the tree
109 m_tree->blockSignals(true);
110 m_tree->setCurrentIndex(QModelIndex());
111 m_tree->blockSignals(false);
112
113 // Emit the signal
114 auto alg = this->getSelectedAlgorithm();
115 emit algorithmSelectionChanged(alg.name, alg.version);
116}
117
118//---------------------------------------------------------------------------
121 auto alg = this->getSelectedAlgorithm();
122 // Select in the combo box
123 m_findAlg->blockSignals(true);
124 m_findAlg->setCurrentIndex(m_findAlg->findText(alg.name, Qt::MatchFixedString));
125 m_findAlg->blockSignals(false);
126 // Emit the signal
127 emit algorithmSelectionChanged(alg.name, alg.version);
128}
129
130//---------------------------------------------------------------------------
140
141//---------------------------------------------------------------------------
148 m_findAlg->blockSignals(true);
149 m_findAlg->setCurrentIndex(m_findAlg->findText(algName, Qt::MatchFixedString));
150 m_findAlg->blockSignals(false);
151 // De-select from the tree
152 m_tree->blockSignals(true);
153 m_tree->setCurrentIndex(QModelIndex());
154 m_tree->blockSignals(false);
155}
156
165
166//============================================================================
167//============================================================================
168//============================================================================
169// Use an anonymous namespace to keep these at file scope
170namespace {
171
172bool AlgorithmDescriptorLess(const AlgorithmDescriptor &d1, const AlgorithmDescriptor &d2) {
173 if (d1.category < d2.category)
174 return true;
175 else if (d1.category == d2.category && d1.name < d2.name)
176 return true;
177 else if (d1.category == d2.category && d1.name == d2.name && d1.version > d2.version)
178 return true;
179
180 return false;
181}
182
183bool AlgorithmDescriptorNameLess(const AlgorithmDescriptor &d1, const AlgorithmDescriptor &d2) {
184 return d1.name < d2.name;
185}
186} // namespace
187
188//============================================================================
189//======================= AlgorithmTreeWidget ================================
190//============================================================================
195 SelectedAlgorithm alg("", 0);
196
197 auto isCategoryName = [](const QTreeWidgetItem *item) {
198 return (item->childCount() != 0 && !item->text(0).contains(" v."));
199 };
200
201 QList<QTreeWidgetItem *> items = this->selectedItems();
202 if (items.size() > 0 && !isCategoryName(items[0])) {
203 QString str = items[0]->text(0);
204 QStringList lst = str.split(" v.");
205 alg.name = lst[0];
206 alg.version = lst[1].toInt();
207 }
208 return alg;
209}
210
211//---------------------------------------------------------------------------
214 if (e->button() == Qt::LeftButton) {
215 if (!itemAt(e->pos()))
216 selectionModel()->clear();
217 m_dragStartPosition = e->pos();
218 }
219
220 QTreeWidget::mousePressEvent(e);
221}
222
223//---------------------------------------------------------------------------
226 if (!(e->buttons() & Qt::LeftButton))
227 return;
228 if ((e->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance())
229 return;
230
231 // Start dragging
232 QDrag *drag = new QDrag(this);
233 auto *mimeData = new QMimeData;
234
235 mimeData->setText("Algorithm");
236 drag->setMimeData(mimeData);
237
238 Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
239 (void)dropAction;
240}
241
242//---------------------------------------------------------------------------
245 auto alg = this->getSelectedAlgorithm();
246 if (!alg.name.isEmpty()) {
247 // Emit the signal that we are executing
248 emit executeAlgorithm(alg.name, alg.version);
249 return;
250 }
251 QTreeWidget::mouseDoubleClickEvent(e);
252}
253
254//---------------------------------------------------------------------------
257 this->clear();
258
259 using AlgNamesType = std::vector<AlgorithmDescriptor>;
260 AlgNamesType names = AlgorithmFactory::Instance().getDescriptors(false, false);
261
262 // sort by category/name/version to fill QTreeWidget
263 sort(names.begin(), names.end(), AlgorithmDescriptorLess);
264
265 QMap<QString, QTreeWidgetItem *> categories; // keeps track of categories added to the tree
266 QMap<QString, QTreeWidgetItem *> algorithms; // keeps track of algorithms
267 // added to the tree (needed in
268 // case there are different
269 // versions of an algorithm)
270
271 for (AlgNamesType::const_iterator i = names.begin(); i != names.end(); ++i) {
272 QString algName = QString::fromStdString(i->name);
273 QString catName = QString::fromStdString(i->category);
274 QStringList subCats = catName.split('\\');
275 if (!categories.contains(catName)) {
276 if (subCats.size() == 1) {
277 QTreeWidgetItem *catItem = new QTreeWidgetItem(QStringList(catName));
278 categories.insert(catName, catItem);
279 this->addTopLevelItem(catItem);
280 } else {
281 QString cn = subCats[0];
282 QTreeWidgetItem *catItem = nullptr;
283 int n = subCats.size();
284 for (int j = 0; j < n; j++) {
285 if (categories.contains(cn)) {
286 catItem = categories[cn];
287 } else {
288 QTreeWidgetItem *newCatItem = new QTreeWidgetItem(QStringList(subCats[j]));
289 categories.insert(cn, newCatItem);
290 if (!catItem) {
291 this->addTopLevelItem(newCatItem);
292 } else {
293 catItem->addChild(newCatItem);
294 }
295 catItem = newCatItem;
296 }
297 if (j != n - 1)
298 cn += "\\" + subCats[j + 1];
299 }
300 }
301 }
302
303 QTreeWidgetItem *algItem = new QTreeWidgetItem(QStringList(algName + " v." + QString::number(i->version)));
304 QString cat_algName = catName + algName;
305 if (!algorithms.contains(cat_algName)) {
306 algorithms.insert(cat_algName, algItem);
307 categories[catName]->addChild(algItem);
308 } else
309 algorithms[cat_algName]->addChild(algItem);
310 }
311}
312
313//============================================================================
314//============================== FindAlgComboBox =============================
315//============================================================================
319 if (e->key() == Qt::Key_Return) {
320 emit enterPressed();
321 return;
322 }
323 QComboBox::keyPressEvent(e);
324}
325
326//---------------------------------------------------------------------------
329 // include hidden categories in the combo list box
330 AlgNamesType names = AlgorithmFactory::Instance().getDescriptors(true, false);
331 addAliases(names);
332
333 // sort by algorithm names only to fill this combobox
334 sort(names.begin(), names.end(), AlgorithmDescriptorNameLess);
335
336 this->clear();
337 std::string prevName = "";
338 for (AlgNamesType::const_iterator i = names.begin(); i != names.end(); ++i) {
339 if (i->name != prevName)
340 this->addItem(QString::fromStdString(i->name));
341 prevName = i->name;
342 }
343 this->setCurrentIndex(-1);
344}
345
348 AlgNamesType aliasList;
349 for (AlgNamesType::const_iterator i = algNamesList.begin(); i != algNamesList.end(); ++i) {
350 // the alias is not empty and is not just different by case from the name
351 if ((!i->alias.empty()) && (!boost::iequals(i->alias, i->name))) {
352 AlgorithmDescriptor newAlias(*i);
353 newAlias.name = i->alias + " [" + i->name + "]";
354 aliasList.emplace_back(newAlias);
355 }
356 }
357 // add them to the list - unsorted
358 algNamesList.reserve(algNamesList.size() + aliasList.size());
359 algNamesList.insert(algNamesList.end(), aliasList.begin(), aliasList.end());
360}
361
363QString FindAlgComboBox::stripAlias(const QString &text) const {
364 QString retVal = text;
365 int foundOpen = text.indexOf("[");
366 if (foundOpen != -1) {
367 int foundClose = text.lastIndexOf("]");
368 if (foundClose != -1)
369 retVal = text.mid(foundOpen + 1, foundClose - foundOpen - 1);
370 }
371 return retVal;
372}
373
374//---------------------------------------------------------------------------
377 // typed selection
378 QString typedText = this->currentText().trimmed(); // text as typed in the combobox
379 if (!typedText.isEmpty()) // if the text is not empty
380 {
381 // find the closest matching entry
382 int matchedIndex = this->findText(typedText, Qt::MatchStartsWith);
383 if (matchedIndex > -1) {
384 typedText = this->itemText(matchedIndex); // text in the combobox at the matched index
385 typedText = stripAlias(typedText);
386 }
387 }
388 return SelectedAlgorithm(typedText, -1);
389}
390
391} // 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.
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.