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"
16
17#include <QCoreApplication>
18#include <QDebug>
19#include <QFileDialog>
20#include <QLabel>
21#include <QMessageBox>
22#include <QPainter>
23#include <QPushButton>
24#include <QTime>
25#include <QVBoxLayout>
26
27namespace MantidQt::API {
28namespace {
30Mantid::Kernel::Logger g_log("ScriptRepositoryView");
31} // namespace
32
33const QString install_mantid_label =
34 "<html><head/><body><p>The <span style=\" font-weight:600;\">"
35 "Script Repository</span> allows you to:</p>"
36 "<p> * Share your scripts and reduction algorithms;</p>"
37 "<p> * Get <span style=\" font-weight:600;\">Mantid</span> Scripts from "
38 "the mantid developers and the community. </p>"
39 "<p><span style=\" font-style:italic;\">"
40 "N.B. The installation usually requires a couple of minutes, depending on "
41 "your network bandwidth. </span></p>"
42 "<p>More Information available at "
43 "<a href=\"http://docs.mantidproject.org/workbench/scriptrepository\"><span style=\" "
44 "text-decoration: underline; color:#0000ff;\">"
45 "http://docs.mantidproject.org/workbench/scriptrepository</span></a></p></br><p><span "
46 "style=\" font-weight:600;\">"
47 "Would you like to install it now?</span></p></body></html>";
48
49const QString installation_in_progress = "<html><head/><body><p><span style=\" font-weight:600;\">"
50 "Installing Script Repository Installation in background!</span></p>"
51 "<p>You may continue to use mantid.</p>"
52 "<p>The Result Log willl give you information of the installation "
53 "progress.</p>"
54 "<p>When finished, please, reopen the <span style=\" "
55 "font-weight:600;\">Script Repository</span>. </p></body></html>";
56
57const QString installation_failed = "<html><head/><body><p>The installation of Script Repository "
58 "<span style=\" font-weight:600;\">Failed</span>!</p>"
59 "<p>Please, check the Result Log to see why the installation failed. "
60 "</p></body></html>";
61
62const QString dir_not_empty_label = "<html><head/><body><p>The directory/folder that you have selected is not "
63 "empty</p>"
64 "<p>Are you sure that you want to install the script repository here? All "
65 "the files and directories found in "
66 "the selected directory/folder could be shared in the repository by "
67 "mistake.</p>"
68 "<p>If you are not sure, please choose 'no' and then select an empty (or "
69 "newly created) directory/folder.</p>"
70 "<p>If this is your home directory, desktop or similar you should "
71 "definitely choose 'no'.</p>"
72 "<p>If you are sure of what you are doing, please choose 'yes'. The "
73 "installation may take a couple of minutes.</p>"
74 "</body></html>";
75
76//----------------------------------------------------------------------------------------------
97
98 ui->setupUi(this);
99
100 try {
101 // create and instance of ScriptRepository
102 Mantid::API::ScriptRepository_sptr repo_ptr = ScriptRepositoryFactory::Instance().create("ScriptRepositoryImpl");
103
104 // check if the ScriptRepository was ever installed
105 if (!repo_ptr->isValid()) {
106 bool successful = chooseLocationAndInstall(std::move(repo_ptr));
107 if (!successful) {
108 return;
109 }
110 }
111 // create the model
112 model = new RepoModel(this);
113
115 // means that the installation failed
116 g_log.warning() << "ScriptRepository installation: " << ex.what() << '\n';
117 g_log.information() << "ScriptRepository installation failed with this information: " << ex.systemError() << '\n';
118 QMessageBox::warning(this, "Installation Failed", QString(ex.what()));
119 close();
120 deleteLater();
121 return;
122 } catch (...) {
123 g_log.error() << "Unknown error occurred to install ScriptRepository. It "
124 "will not be shown.\n";
125 close();
126 deleteLater();
127 return;
128 }
129 // from this point, it is assumed that ScriptRepository is installed.
130
131 // configure the Ui
132 connect(ui->reloadPushButton, SIGNAL(clicked()), this, SLOT(updateModel()));
133 connect(ui->pbHelp, SIGNAL(clicked()), this, SLOT(helpClicked()));
134 connect(model, SIGNAL(executingThread(bool)), ui->reloadPushButton, SLOT(setDisabled(bool)));
135
136 // setup the model and delegates
137 ui->repo_treeView->setModel(model);
138 ui->repo_treeView->setItemDelegateForColumn(1, new RepoDelegate(this));
139 ui->repo_treeView->setItemDelegateForColumn(2, new CheckBoxDelegate(this));
140 ui->repo_treeView->setItemDelegateForColumn(3, new RemoveEntryDelegate(this));
141 ui->repo_treeView->setColumnWidth(0, 290);
142
143 // stablish the connections.
144 connect(ui->repo_treeView, SIGNAL(activated(const QModelIndex &)), this, SLOT(cell_activated(const QModelIndex &)));
145 connect(ui->repo_treeView, SIGNAL(currentCell(const QModelIndex &)), this, SLOT(currentChanged(const QModelIndex &)));
146 const ConfigServiceImpl &config = ConfigService::Instance();
147 updateLocationString(config.getString("ScriptLocalRepository"));
148 connect(ui->folderPathLabel, SIGNAL(linkActivated(QString)), this, SLOT(openFolderLink(QString)));
149}
150
160 enum EXC_OPTIONS { NOTWANTED, NODIRECTORY };
161
162 QString dir;
163 try {
164 // Ask the user if he wants to install the ScriptRepository
165 if (QMessageBox::Ok != QMessageBox::question(this, "Install Script Repository?", install_mantid_label,
166 QMessageBox::Ok | QMessageBox::Cancel)) {
167 throw NOTWANTED;
168 }
169 // get the directory to install the script repository
170 const ConfigServiceImpl &config = ConfigService::Instance();
171 QString loc = QString::fromStdString(config.getString("ScriptLocalRepository"));
172
173 bool sureAboutDir = false;
174
175 while (!sureAboutDir) {
176 dir = QFileDialog::getExistingDirectory(this, tr("Where do you want to install Script Repository?"), loc,
177 QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
178
179 // configuring
180 if (dir.isEmpty()) {
181 throw NODIRECTORY;
182 }
183
184 // warn if dir is not empty
185 if (0 == QDir(dir).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot).count()) {
186 // empty dir, just go ahead
187 sureAboutDir = true;
188 } else {
189 // warn user in case the repo is being installed in its home, etc.
190 // directory
191 QMessageBox::StandardButton sel =
192 QMessageBox::question(this, "Are you sure you want to install the Script Repository here?",
193 dir_not_empty_label, QMessageBox::Yes | QMessageBox::No);
194 if (QMessageBox::Yes == sel)
195 sureAboutDir = true;
196 }
197 }
198 } catch (EXC_OPTIONS &ex) {
199 if (ex == NODIRECTORY) {
200 // probably the user change mind. He does not want to install any more.
201 QMessageBox::warning(this, "Installation Failed", "Invalid Folder to install Script Repository!\n");
202 }
203
204 close();
205 deleteLater();
206 return false;
207 }
208 // attempt to install
209 repo_ptr->install(dir.toStdString());
210 updateLocationString(dir.toStdString());
211 g_log.information() << "ScriptRepository installed at " << dir.toStdString() << '\n';
212 return true;
213}
214
215void ScriptRepositoryView::updateLocationString(const std::string &installDir) {
216 const QString loc = QString::fromStdString(installDir);
217 const QString loc_info = "<html><head/><body><p><a href=\"%1\"><span style=\" "
218 "text-decoration: underline; "
219 "color:#0000ff;\">%2</span></a></p></body></html>";
220 QString path_label;
221 if (loc.size() < 50)
222 path_label = loc;
223 else {
224 path_label = QString("%1...%2").arg(loc.left(20)).arg(loc.right(27));
225 }
226
227 ui->folderPathLabel->setText(loc_info.arg(loc).arg(path_label));
228 ui->folderPathLabel->setToolTip(QString("Click here to open Script Repository Folder: %1.").arg(loc));
229}
230
239 RepoModel *before = model;
240 auto model_repo_ptr = model->getRepoPtr();
241 if (!model_repo_ptr->isValid()) {
242 const bool success = chooseLocationAndInstall(std::move(model_repo_ptr));
243 if (!success) {
244 return;
245 }
246 }
247 model = new RepoModel();
248 connect(model, SIGNAL(executingThread(bool)), ui->reloadPushButton, SLOT(setDisabled(bool)));
249 ui->repo_treeView->setModel(model);
250 delete before;
251}
252
253//----------------------------------------------------------------------------------------------
257
264void ScriptRepositoryView::cell_activated(const QModelIndex &in) {
265
266 RepoModel *_model = qobject_cast<RepoModel *>(ui->repo_treeView->model());
267 if (_model) {
268 QString path = _model->filePath(in);
269 if (path.isEmpty()) {
270 // no real file to be opened.
271 return;
272 }
273 emit loadScript(path);
274 }
275}
276
283void ScriptRepositoryView::currentChanged(const QModelIndex &in) {
284 RepoModel *_model = qobject_cast<RepoModel *>(ui->repo_treeView->model());
285 if (_model) {
286 // try to get the description of the file pointed at the current index.
287 // and update the description text browser.
288 QString description = _model->fileDescription(in);
289 ui->desc_textBrowser->setText(description);
290 QString author_name = _model->author(in);
291 if (author_name.isEmpty())
292 ui->authorNameLabel->setText("");
293 else
294 ui->authorNameLabel->setText(QString("<b>Author:</b> ") + author_name);
295 return;
296 }
297}
298
300void ScriptRepositoryView::helpClicked() { HelpWindow::showPage(QStringLiteral("workbench/scriptrepository.html")); }
301
303// DELEGATE : Allow to display and interact with the View in a nicer way.
304// Improve the User Experience.
306
307ScriptRepositoryView::RepoDelegate::RepoDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
332void ScriptRepositoryView::RepoDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
333 const QModelIndex &index) const {
334
335 if (!index.isValid())
336 return;
337 if (painter->device() == nullptr)
338 return;
339
340 // get the state and chose the best fit icon
341 QString state = index.model()->data(index, Qt::DisplayRole).toString();
342 auto icon = getIcon(state);
343
344 // define the region to draw the icon
345 QRect buttonRect(option.rect);
346 int min_val = buttonRect.width() < buttonRect.height() ? buttonRect.width() : buttonRect.height();
347 // make it square
348 buttonRect.setWidth(min_val);
349 buttonRect.setHeight(min_val);
350 buttonRect.moveCenter(option.rect.center());
351
352 // define the options to draw a push button with the icon displayed
353 QStyleOptionButton button;
354 button.rect = buttonRect;
355 button.icon = icon;
356 int icon_size = (int)(min_val * .8);
357 button.iconSize = QSize(icon_size, icon_size);
358 button.state = QStyle::State_Enabled;
359 // draw a push button
360 QApplication::style()->drawControl(QStyle::CE_PushButton, &button, painter);
361}
362
363QIcon ScriptRepositoryView::RepoDelegate::getIcon(const QString &state) const {
364 QIcon icon;
365 if (state == RepoModel::remoteOnlySt())
366 icon = Icons::getIcon("mdi.download");
367 else if (state == RepoModel::remoteChangedSt() || state == RepoModel::bothChangedSt()) {
368 icon = Icons::getIcon("mdi.transfer-down");
369 } else if (state == RepoModel::updatedSt())
370 icon = Icons::getIcon("mdi.check-bold");
371 else if (state == RepoModel::localOnlySt() || state == RepoModel::localChangedSt())
372 icon = Icons::getIcon("mdi.upload");
373 else if (state == RepoModel::downloadSt())
374 icon = Icons::getIcon("mdi.progress-download");
375 else if (state == RepoModel::uploadSt())
376 icon = Icons::getIcon("mdi.progress-upload");
377 return icon;
378}
379
402bool ScriptRepositoryView::RepoDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
403 const QStyleOptionViewItem & /*option*/,
404 const QModelIndex &index) {
405 // if event is mouse click
406 if (event->type() == QEvent::MouseButtonPress) {
407 QString value = model->data(index, Qt::DisplayRole).toString();
408 QString action = "Download";
410 action = "Upload";
412 return false; // ignore
413 return model->setData(index, action, Qt::EditRole);
414 } else {
415 return true; // Does not allow others events to be processed (example:
416 // double-click)
417 }
418}
422QSize ScriptRepositoryView::RepoDelegate::sizeHint(const QStyleOptionViewItem & /*option*/,
423 const QModelIndex & /*index*/) const {
424 return QSize(35, 35);
425}
426
428// CheckBoxDelegate
430
431ScriptRepositoryView::CheckBoxDelegate::CheckBoxDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
453void ScriptRepositoryView::CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
454 const QModelIndex &index) const {
455 if (!index.isValid())
456 return;
457 if (painter->device() == nullptr)
458 return;
459
460 QStyleOptionViewItem modifiedOption(option);
461
462 QPoint p = modifiedOption.rect.center();
463 QSize curr = modifiedOption.rect.size();
464 int min_value = (int)((curr.width() < curr.height()) ? curr.width() : curr.height() * .8);
465 // make the checkbox a square in the center of the cell
466 modifiedOption.rect.setSize(QSize(min_value, min_value));
467 modifiedOption.rect.moveCenter(p);
468 // get the current state of this entry
469 QString state = index.model()->data(index, Qt::DisplayRole).toString();
470
471 if (state == "true")
472 modifiedOption.state |= QStyle::State_On;
473 else if (state == "false")
474 modifiedOption.state |= QStyle::State_Off;
475 else
476 return;
477 // draw it
478 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &modifiedOption, painter);
479}
480
503bool ScriptRepositoryView::CheckBoxDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
504 const QStyleOptionViewItem & /*option*/,
505 const QModelIndex &index) {
506 if (event->type() == QEvent::MouseButtonPress) {
507 QString value = model->data(index, Qt::DisplayRole).toString();
508 QString action = "setFalse";
509 if (value == "false")
510 action = "setTrue";
511 return model->setData(index, action, Qt::EditRole);
512 } else {
513 // QStyledItemDelegate::editorEvent(event, model, option, index);
514 return true; // Does not allow the event to be catched by another one
515 }
516}
518// RemoveEntryDelegate
520
521ScriptRepositoryView::RemoveEntryDelegate::RemoveEntryDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
535void ScriptRepositoryView::RemoveEntryDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
536 const QModelIndex &index) const {
537
538 if (!index.isValid())
539 return;
540 if (painter->device() == nullptr)
541 return;
542
543 QIcon icon;
544 // get the state and chose the best fit icon
545 QString entry_type = index.model()->data(index, Qt::DisplayRole).toString();
546
547 if (entry_type == "protected")
548 return;
549
550 icon = Icons::getIcon("mdi.trash-can");
551
552 // define the region to draw the icon
553 QRect buttonRect(option.rect);
554 int min_val = buttonRect.width() < buttonRect.height() ? buttonRect.width() : buttonRect.height();
555 // make it square
556 buttonRect.setWidth(min_val);
557 buttonRect.setHeight(min_val);
558 buttonRect.moveCenter(option.rect.center());
559
560 // define the options to draw a push button with the icon displayed
561 QStyleOptionButton button;
562 button.rect = buttonRect;
563 button.icon = icon;
564 int icon_size = (int)(min_val * .8);
565 button.iconSize = QSize(icon_size, icon_size);
566 button.state = QStyle::State_Enabled;
567 // draw a push button
568 QApplication::style()->drawControl(QStyle::CE_PushButton, &button, painter);
569}
570
590bool ScriptRepositoryView::RemoveEntryDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
591 const QStyleOptionViewItem & /*option*/,
592 const QModelIndex &index) {
593 // if event is mouse click
594 if (event->type() == QEvent::MouseButtonPress) {
595 QString entry = index.model()->data(index, Qt::DisplayRole).toString();
596 if (entry == "protected")
597 return true;
598 QString action = "delete";
599 return model->setData(index, action, Qt::EditRole);
600 } else {
601 return true; // Does not allow others events to be processed (example:
602 // double-click)
603 }
604}
605
611void ScriptRepositoryView::openFolderLink(const QString &link) {
612 const std::string error_msg = "Unable to open \"" + link.toStdString() + "\". Reason: ";
613
614 // QUrl::fromLocalFile seems to be the most robust way of constructing QUrls
615 // on
616 // the local file system for all platforms.
617 const QUrl url = QUrl::fromLocalFile(link);
618 if (!url.isValid()) {
619 g_log.error() << error_msg << "Invalid (malformed) URL.\n";
620 return;
621 }
622
623 const bool openSuccessful = MantidDesktopServices::openUrl(url);
624 if (!openSuccessful)
625 g_log.error() << error_msg << "Could not find directory.\n";
626}
627
628} // namespace MantidQt::API
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
int count
counter
Definition Matrix.cpp:37
static void showPage(const std::string &url=std::string())
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()
QString filePath(const QModelIndex &index)
Return the operative system file path if it exists.
Mantid::API::ScriptRepository_sptr getRepoPtr()
Definition RepoModel.h:198
QString author(const QModelIndex &index)
static const QString & remoteChangedSt()
static const QString & updatedSt()
static const QString & uploadSt()
static const QString & bothChangedSt()
QVariant data(const QModelIndex &index, int role) const override
access to the ScriptRepository data
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
change data
QString fileDescription(const QModelIndex &index)
Return the description of the file for a defined entry.
static const QString & localOnlySt()
static const QString & downloadSt()
static const QString & remoteOnlySt()
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.
bool chooseLocationAndInstall(Mantid::API::ScriptRepository_sptr repo_ptr)
Prompt the user with where to install the script repository, and install it.
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.
void updateLocationString(const std::string &installDir)
The ScriptRepository class is intended to be used mainly by the users, who will be willing to share a...
const char * what() const noexcept override
Returns the message string.
const std::string & systemError() const
Returns the error description with technical details on the origin and cause.
The ConfigService class provides a simple facade to access the Configuration functionality of the Man...
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
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
Mantid::Kernel::SingletonHolder< ScriptRepositoryFactoryImpl > ScriptRepositoryFactory
Kernel::Logger g_log("DetermineSpinStateOrder")
Mantid::Kernel::SingletonHolder< ConfigServiceImpl > ConfigService