Mantid
Loading...
Searching...
No Matches
ScriptRepositoryView.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 +
11#include "MantidKernel/Logger.h"
12#include "MantidQtIcons/Icon.h"
15
16#include <QCoreApplication>
17#include <QDebug>
18#include <QFileDialog>
19#include <QLabel>
20#include <QMessageBox>
21#include <QPainter>
22#include <QPushButton>
23#include <QTime>
24#include <QVBoxLayout>
25
26namespace MantidQt::API {
27namespace {
29Mantid::Kernel::Logger g_log("ScriptRepositoryView");
30} // namespace
31
32const QString install_mantid_label = "<html><head/><body><p>The <span style=\" font-weight:600;\">"
33 "Script Repository</span> allows you to:</p>"
34 "<p> * Share your scripts and reduction algorithms;</p>"
35 "<p> * Get <span style=\" font-weight:600;\">Mantid</span> Scripts from "
36 "the mantid developers and the community. </p>"
37 "<p><span style=\" font-style:italic;\">"
38 "N.B. The installation usually requires a couple of minutes, depending on "
39 "your network bandwidth. </span></p>"
40 "<p>More Information available at "
41 "<a href=\"http://www.mantidproject.org/ScriptRepository\"><span style=\" "
42 "text-decoration: underline; color:#0000ff;\">"
43 "http://www.mantidproject.org/ScriptRepository</span></a></p></br><p><span "
44 "style=\" font-weight:600;\">"
45 "Would you like to install it now?</span></p></body></html>";
46
47const QString installation_in_progress = "<html><head/><body><p><span style=\" font-weight:600;\">"
48 "Installing Script Repository Installation in background!</span></p>"
49 "<p>You may continue to use mantid.</p>"
50 "<p>The Result Log willl give you information of the installation "
51 "progress.</p>"
52 "<p>When finished, please, reopen the <span style=\" "
53 "font-weight:600;\">Script Repository</span>. </p></body></html>";
54
55const QString installation_failed = "<html><head/><body><p>The installation of Script Repository "
56 "<span style=\" font-weight:600;\">Failed</span>!</p>"
57 "<p>Please, check the Result Log to see why the installation failed. "
58 "</p></body></html>";
59
60const QString dir_not_empty_label = "<html><head/><body><p>The directory/folder that you have selected is not "
61 "empty</p>"
62 "<p>Are you sure that you want to install the script repository here? All "
63 "the files and directories found in "
64 "the selected directory/folder could be shared in the repository by "
65 "mistake.</p>"
66 "<p>If you are not sure, please choose 'no' and then select an empty (or "
67 "newly created) directory/folder.</p>"
68 "<p>If this is your home directory, desktop or similar you should "
69 "definitely choose 'no'.</p>"
70 "<p>If you are sure of what you are doing, please choose 'yes'. The "
71 "installation may take a couple of minutes.</p>"
72 "</body></html>";
73
74//----------------------------------------------------------------------------------------------
95 enum EXC_OPTIONS { NOTWANTED, NODIRECTORY };
96
97 try {
98
99 // create and instance of ScriptRepository
100 Mantid::API::ScriptRepository_sptr repo_ptr = ScriptRepositoryFactory::Instance().create("ScriptRepositoryImpl");
101
102 // check if the ScriptRepository was ever installed
103 if (!repo_ptr->isValid()) {
104 // No. It has never been installed.
105 // Ask the user if he wants to install the ScriptRepository
106 if (QMessageBox::Ok != QMessageBox::question(this, "Install Script Repository?", install_mantid_label,
107 QMessageBox::Ok | QMessageBox::Cancel)) {
108 throw NOTWANTED;
109 }
110 // get the directory to install the script repository
111 ConfigServiceImpl &config = ConfigService::Instance();
112 QString loc = QString::fromStdString(config.getString("ScriptLocalRepository"));
113
114 bool sureAboutDir = false;
115
116 QString dir;
117 while (!sureAboutDir) {
118 dir = QFileDialog::getExistingDirectory(this, tr("Where do you want to install Script Repository?"), loc,
119 QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
120
121 // configuring
122 if (dir.isEmpty()) {
123 throw NODIRECTORY;
124 }
125
126 // warn if dir is not empty
127 if (0 == QDir(dir).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot).count()) {
128 // empty dir, just go ahead
129 sureAboutDir = true;
130 } else {
131 // warn user in case the repo is being installed in its home, etc.
132 // directory
133 QMessageBox::StandardButton sel =
134 QMessageBox::question(this, "Are you sure you want to install the Script Repository here?",
135 dir_not_empty_label, QMessageBox::Yes | QMessageBox::No);
136 if (QMessageBox::Yes == sel)
137 sureAboutDir = true;
138 }
139 }
140
141 // attempt to install
142 repo_ptr->install(dir.toStdString());
143 g_log.information() << "ScriptRepository installed at " << dir.toStdString() << '\n';
144 }
145 // create the model
146 model = new RepoModel(this);
147
148 } catch (EXC_OPTIONS &ex) {
149 if (ex == NODIRECTORY)
150 // probably the user change mind. He does not want to install any more.
151 QMessageBox::warning(this, "Installation Failed", "Invalid Folder to install Script Repository!\n");
152
153 close();
154 deleteLater();
155 return;
157 // means that the installation failed
158 g_log.warning() << "ScriptRepository installation: " << ex.what() << '\n';
159 g_log.information() << "ScriptRepository installation failed with this information: " << ex.systemError() << '\n';
160 QMessageBox::warning(this, "Installation Failed", QString(ex.what()));
161 close();
162 deleteLater();
163 return;
164 } catch (...) {
165 g_log.error() << "Unknown error occurred to install ScriptRepository. It "
166 "will not be shown.\n";
167 close();
168 deleteLater();
169 return;
170 }
171 // from this point, it is assumed that ScriptRepository is installed.
172
173 // configure the Ui
174 ui->setupUi(this);
175 connect(ui->reloadPushButton, SIGNAL(clicked()), this, SLOT(updateModel()));
176 connect(ui->pbHelp, SIGNAL(clicked()), this, SLOT(helpClicked()));
177 connect(model, SIGNAL(executingThread(bool)), ui->reloadPushButton, SLOT(setDisabled(bool)));
178
179 // setup the model and delegates
180 ui->repo_treeView->setModel(model);
181 ui->repo_treeView->setItemDelegateForColumn(1, new RepoDelegate(this));
182 ui->repo_treeView->setItemDelegateForColumn(2, new CheckBoxDelegate(this));
183 ui->repo_treeView->setItemDelegateForColumn(3, new RemoveEntryDelegate(this));
184 ui->repo_treeView->setColumnWidth(0, 290);
185
186 // stablish the connections.
187 connect(ui->repo_treeView, SIGNAL(activated(const QModelIndex &)), this, SLOT(cell_activated(const QModelIndex &)));
188 connect(ui->repo_treeView, SIGNAL(currentCell(const QModelIndex &)), this, SLOT(currentChanged(const QModelIndex &)));
189
190 const ConfigServiceImpl &config = ConfigService::Instance();
191 const QString loc = QString::fromStdString(config.getString("ScriptLocalRepository"));
192 const QString loc_info = "<html><head/><body><p><a href=\"%1\"><span style=\" "
193 "text-decoration: underline; "
194 "color:#0000ff;\">%2</span></a></p></body></html>";
195 QString path_label;
196 if (loc.size() < 50)
197 path_label = loc;
198 else {
199 path_label = QString("%1...%2").arg(loc.left(20)).arg(loc.right(27));
200 }
201
202 ui->folderPathLabel->setText(loc_info.arg(loc).arg(path_label));
203 ui->folderPathLabel->setToolTip(QString("Click here to open Script Repository Folder: %1.").arg(loc));
204 connect(ui->folderPathLabel, SIGNAL(linkActivated(QString)), this, SLOT(openFolderLink(QString)));
205}
206
215 RepoModel *before = model;
216 model = new RepoModel();
217 connect(model, SIGNAL(executingThread(bool)), ui->reloadPushButton, SLOT(setDisabled(bool)));
218 ui->repo_treeView->setModel(model);
219 delete before;
220}
221
222//----------------------------------------------------------------------------------------------
226
233void ScriptRepositoryView::cell_activated(const QModelIndex &in) {
234
235 RepoModel *_model = qobject_cast<RepoModel *>(ui->repo_treeView->model());
236 if (_model) {
237 QString path = _model->filePath(in);
238 if (path.isEmpty()) {
239 // no real file to be opened.
240 return;
241 }
242 emit loadScript(path);
243 }
244}
245
252void ScriptRepositoryView::currentChanged(const QModelIndex &in) {
253 RepoModel *_model = qobject_cast<RepoModel *>(ui->repo_treeView->model());
254 if (_model) {
255 // try to get the description of the file pointed at the current index.
256 // and update the description text browser.
257 QString description = _model->fileDescription(in);
258 ui->desc_textBrowser->setText(description);
259 QString author_name = _model->author(in);
260 if (author_name.isEmpty())
261 ui->authorNameLabel->setText("");
262 else
263 ui->authorNameLabel->setText(QString("<b>Author:</b> ") + author_name);
264 return;
265 }
266}
267
270 MantidDesktopServices::openUrl(QUrl("http://www.mantidproject.org/ScriptRepository"));
271}
272
274// DELEGATE : Allow to display and interact with the View in a nicer way.
275// Improve the User Experience.
277
278ScriptRepositoryView::RepoDelegate::RepoDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
303void ScriptRepositoryView::RepoDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
304 const QModelIndex &index) const {
305
306 if (!index.isValid())
307 return;
308 if (painter->device() == nullptr)
309 return;
310
311 // get the state and chose the best fit icon
312 QString state = index.model()->data(index, Qt::DisplayRole).toString();
313 auto icon = getIcon(state);
314
315 // define the region to draw the icon
316 QRect buttonRect(option.rect);
317 int min_val = buttonRect.width() < buttonRect.height() ? buttonRect.width() : buttonRect.height();
318 // make it square
319 buttonRect.setWidth(min_val);
320 buttonRect.setHeight(min_val);
321 buttonRect.moveCenter(option.rect.center());
322
323 // define the options to draw a push button with the icon displayed
324 QStyleOptionButton button;
325 button.rect = buttonRect;
326 button.icon = icon;
327 int icon_size = (int)(min_val * .8);
328 button.iconSize = QSize(icon_size, icon_size);
329 button.state = QStyle::State_Enabled;
330 // draw a push button
331 QApplication::style()->drawControl(QStyle::CE_PushButton, &button, painter);
332}
333
334QIcon ScriptRepositoryView::RepoDelegate::getIcon(const QString &state) const {
335 QIcon icon;
336 if (state == RepoModel::remoteOnlySt())
337 icon = Icons::getIcon("mdi.download");
338 else if (state == RepoModel::remoteChangedSt() || state == RepoModel::bothChangedSt()) {
339 icon = Icons::getIcon("mdi.transfer-down");
340 } else if (state == RepoModel::updatedSt())
341 icon = Icons::getIcon("mdi.check-bold");
342 else if (state == RepoModel::localOnlySt() || state == RepoModel::localChangedSt())
343 icon = Icons::getIcon("mdi.upload");
344 else if (state == RepoModel::downloadSt())
345 icon = Icons::getIcon("mdi.progress-download");
346 else if (state == RepoModel::uploadSt())
347 icon = Icons::getIcon("mdi.progress-upload");
348 return icon;
349}
350
373bool ScriptRepositoryView::RepoDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
374 const QStyleOptionViewItem & /*option*/,
375 const QModelIndex &index) {
376 // if event is mouse click
377 if (event->type() == QEvent::MouseButtonPress) {
378 QString value = model->data(index, Qt::DisplayRole).toString();
379 QString action = "Download";
381 action = "Upload";
383 return false; // ignore
384 return model->setData(index, action, Qt::EditRole);
385 } else {
386 return true; // Does not allow others events to be processed (example:
387 // double-click)
388 }
389}
393QSize ScriptRepositoryView::RepoDelegate::sizeHint(const QStyleOptionViewItem & /*option*/,
394 const QModelIndex & /*index*/) const {
395 return QSize(35, 35);
396}
397
399// CheckBoxDelegate
401
402ScriptRepositoryView::CheckBoxDelegate::CheckBoxDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
424void ScriptRepositoryView::CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
425 const QModelIndex &index) const {
426 if (!index.isValid())
427 return;
428 if (painter->device() == nullptr)
429 return;
430
431 QStyleOptionViewItem modifiedOption(option);
432
433 QPoint p = modifiedOption.rect.center();
434 QSize curr = modifiedOption.rect.size();
435 int min_value = (int)((curr.width() < curr.height()) ? curr.width() : curr.height() * .8);
436 // make the checkbox a square in the center of the cell
437 modifiedOption.rect.setSize(QSize(min_value, min_value));
438 modifiedOption.rect.moveCenter(p);
439 // get the current state of this entry
440 QString state = index.model()->data(index, Qt::DisplayRole).toString();
441
442 if (state == "true")
443 modifiedOption.state |= QStyle::State_On;
444 else if (state == "false")
445 modifiedOption.state |= QStyle::State_Off;
446 else
447 return;
448 // draw it
449 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &modifiedOption, painter);
450}
451
474bool ScriptRepositoryView::CheckBoxDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
475 const QStyleOptionViewItem & /*option*/,
476 const QModelIndex &index) {
477 if (event->type() == QEvent::MouseButtonPress) {
478 QString value = model->data(index, Qt::DisplayRole).toString();
479 QString action = "setFalse";
480 if (value == "false")
481 action = "setTrue";
482 return model->setData(index, action, Qt::EditRole);
483 } else {
484 // QStyledItemDelegate::editorEvent(event, model, option, index);
485 return true; // Does not allow the event to be catched by another one
486 }
487}
489// RemoveEntryDelegate
491
492ScriptRepositoryView::RemoveEntryDelegate::RemoveEntryDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
506void ScriptRepositoryView::RemoveEntryDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
507 const QModelIndex &index) const {
508
509 if (!index.isValid())
510 return;
511 if (painter->device() == nullptr)
512 return;
513
514 QIcon icon;
515 // get the state and chose the best fit icon
516 QString entry_type = index.model()->data(index, Qt::DisplayRole).toString();
517
518 if (entry_type == "protected")
519 return;
520
521 icon = Icons::getIcon("mdi.trash-can");
522
523 // define the region to draw the icon
524 QRect buttonRect(option.rect);
525 int min_val = buttonRect.width() < buttonRect.height() ? buttonRect.width() : buttonRect.height();
526 // make it square
527 buttonRect.setWidth(min_val);
528 buttonRect.setHeight(min_val);
529 buttonRect.moveCenter(option.rect.center());
530
531 // define the options to draw a push button with the icon displayed
532 QStyleOptionButton button;
533 button.rect = buttonRect;
534 button.icon = icon;
535 int icon_size = (int)(min_val * .8);
536 button.iconSize = QSize(icon_size, icon_size);
537 button.state = QStyle::State_Enabled;
538 // draw a push button
539 QApplication::style()->drawControl(QStyle::CE_PushButton, &button, painter);
540}
541
561bool ScriptRepositoryView::RemoveEntryDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
562 const QStyleOptionViewItem & /*option*/,
563 const QModelIndex &index) {
564 // if event is mouse click
565 if (event->type() == QEvent::MouseButtonPress) {
566 QString entry = index.model()->data(index, Qt::DisplayRole).toString();
567 if (entry == "protected")
568 return true;
569 QString action = "delete";
570 return model->setData(index, action, Qt::EditRole);
571 } else {
572 return true; // Does not allow others events to be processed (example:
573 // double-click)
574 }
575}
576
582void ScriptRepositoryView::openFolderLink(const QString &link) {
583 const std::string error_msg = "Unable to open \"" + link.toStdString() + "\". Reason: ";
584
585 // QUrl::fromLocalFile seems to be the most robust way of constructing QUrls
586 // on
587 // the local file system for all platforms.
588 const QUrl url = QUrl::fromLocalFile(link);
589 if (!url.isValid()) {
590 g_log.error() << error_msg << "Invalid (malformed) URL.\n";
591 return;
592 }
593
594 const bool openSuccessful = MantidDesktopServices::openUrl(url);
595 if (!openSuccessful)
596 g_log.error() << error_msg << "Could not find directory.\n";
597}
598
599} // namespace MantidQt::API
double value
The value of the point.
Definition: FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
int count
counter
Definition: Matrix.cpp:37
static bool openUrl(const QUrl &url)
Opens a url in the appropriate web browser.
RepoModel : Wrapper for ScriptRepository to fit the Model View Qt Framework.
Definition: RepoModel.h:75
static const QString & localChangedSt()
Definition: RepoModel.cpp:937
QString filePath(const QModelIndex &index)
Return the operative system file path if it exists.
Definition: RepoModel.cpp:727
QString author(const QModelIndex &index)
Definition: RepoModel.cpp:710
static const QString & remoteChangedSt()
Definition: RepoModel.cpp:939
static const QString & updatedSt()
Definition: RepoModel.cpp:941
static const QString & uploadSt()
Definition: RepoModel.cpp:947
static const QString & bothChangedSt()
Definition: RepoModel.cpp:943
QVariant data(const QModelIndex &index, int role) const override
access to the ScriptRepository data
Definition: RepoModel.cpp:212
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
change data
Definition: RepoModel.cpp:384
QString fileDescription(const QModelIndex &index)
Return the description of the file for a defined entry.
Definition: RepoModel.cpp:697
static const QString & localOnlySt()
Definition: RepoModel.cpp:933
static const QString & downloadSt()
Definition: RepoModel.cpp:945
static const QString & remoteOnlySt()
Definition: RepoModel.cpp:935
Delegate to show the checkbox for configuring the auto update.
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
Reacts to the iteraction with the user when he clicks on the buttons displayed at paint.
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Draws the column 2 (AutoUpdate) of ScriptRepositoryView.
Delegate to show the icon to remove the entry from the local and central repository.
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Draws the column 3 (delete) of ScriptRepositoryView.
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
Reacts to the iteraction with the user when he clicks on the buttons displayed at paint.
Delegate to show the icons Download and Upload.
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
Reacts to the iteraction with the user when he clicks on the buttons displayed at paint.
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Draws the column 1 (Status) of ScriptRepositoryView.
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
Provides the ideal size for this column.
ScriptRepositoryView : Provide the User Interface to the ScriptRepository.
ScriptRepositoryView(QWidget *parent=nullptr)
Creates the widget for the ScriptRepositoryView.
void openFolderLink(const QString &)
Attempt to open the given folder link using an appropriate application.
void helpClicked()
Open the ScriptRepository Page on Web Browser.
void currentChanged(const QModelIndex &current)
This method will be executed every time the user change the selection.
void updateModel()
This method refreshes the ScriptRepository and allows it to check list the files again.
void cell_activated(const QModelIndex &)
Allows the user to open a file to investigate it.
The ScriptRepository class is intended to be used mainly by the users, who will be willing to share a...
The ConfigService class provides a simple facade to access the Configuration functionality of the Man...
Definition: ConfigService.h:63
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition: Logger.h:52
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
Manage the lifetime of a class intended to be a singleton.
const QString installation_in_progress
const QString install_mantid_label
const QString dir_not_empty_label
const QString installation_failed
std::shared_ptr< ScriptRepository > ScriptRepository_sptr
shared pointer to the function base class
Kernel::Logger g_log("ExperimentInfo")
static logger object