BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
JobListView.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Job/JobListView.cpp
6 //! @brief Implements class JobListView
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
17 #include "GUI/Model/Job/JobItem.h"
22 #include <QAction>
23 #include <QListView>
24 #include <QMenu>
25 #include <QVBoxLayout>
26 
27 namespace {
28 
29 //! compare function for sorting indexes according to row descending
30 bool row_descending(const QModelIndex& idx1, const QModelIndex& idx2)
31 {
32  return idx1.row() > idx2.row();
33 }
34 
35 //! compare function for sorting indexes according to row asscending
36 bool row_ascending(const QModelIndex& idx1, const QModelIndex& idx2)
37 {
38  return idx1.row() < idx2.row();
39 }
40 
41 } // namespace
42 
43 //==================================================================================================
44 // JobListView
45 //==================================================================================================
46 
47 //--------------------------------------------------------------------------------------------------
48 // public member functions
49 //--------------------------------------------------------------------------------------------------
50 
51 JobListView::JobListView(JobModel* jobs, QWidget* parent, Qt::WindowFlags f)
52  : QWidget(parent, f)
53 {
54  setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
55 
56  auto* layout = new QVBoxLayout(this);
57  layout->setMargin(0);
58 
59  m_runAction = new QAction("Run", this);
60  m_runAction->setIcon(QIcon(":/images/play.svg"));
61  m_runAction->setToolTip("Run currently selected jobs");
62  connect(m_runAction, &QAction::triggered, this, &JobListView::onRun);
63  addAction(m_runAction);
64 
65  m_cancelAction = new QAction("Stop", this);
66  m_cancelAction->setIcon(QIcon(":/images/stop.svg"));
67  m_cancelAction->setToolTip("Stop currently selected jobs");
68  connect(m_cancelAction, &QAction::triggered, this, &JobListView::onCancel);
69  addAction(m_cancelAction);
70 
71  m_removeAction = new QAction("Remove", this);
72  m_removeAction->setIcon(QIcon(":/images/delete.svg"));
73  m_removeAction->setToolTip("Remove currently selected jobs");
74  connect(m_removeAction, &QAction::triggered, this, &JobListView::onRemove);
75  addAction(m_removeAction);
76 
77  m_equalizeMenu = new QMenu("Equalize selected plots", this);
78 
79  QToolBar* toolbar = new StyledToolbar(this);
80  toolbar->setMinimumSize(toolbar->minimumHeight(), toolbar->minimumHeight());
81  toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
82  toolbar->addAction(m_runAction);
83  toolbar->addAction(m_cancelAction);
84  toolbar->addAction(m_removeAction);
85  layout->addWidget(toolbar);
86 
87  m_listView = new QListView(this);
88  m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
90  m_listView->setItemDelegate(m_listViewDelegate);
91  layout->addWidget(m_listView);
92 
93  m_model = new JobListModel(jobs, this);
94  m_listView->setModel(m_model);
95 
96  setContextMenuPolicy(Qt::CustomContextMenu);
97  connect(this, &QWidget::customContextMenuRequested, this, &JobListView::showContextMenu);
98 
99  connect(m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
101  connect(m_model, &QAbstractListModel::dataChanged, this,
103 
104  updateActions();
106 }
107 
108 QVector<JobItem*> JobListView::selectedJobs() const
109 {
110  QVector<JobItem*> jobs;
111  for (const QModelIndex& index : m_listView->selectionModel()->selectedIndexes())
112  jobs.push_back(m_model->jobForIndex(index));
113  return jobs;
114 }
115 
117 {
118  QModelIndex idx = m_model->indexForJob(job);
119  QModelIndexList selected = m_listView->selectionModel()->selectedIndexes();
120 
121  // Already selected, but we still will emit the signal to notify widgets.
122  // To handle the case, when the job was selected before it completed (and some stack widgets
123  // were refusing to show the content for non-complete job).
124  if (selected.size() == 1 && selected.front() == idx) {
125  emit selectedJobsChanged({job});
126  return;
127  }
128 
129  m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
130 }
131 
132 //--------------------------------------------------------------------------------------------------
133 // private slots
134 //--------------------------------------------------------------------------------------------------
136 {
137  updateActions();
138 
140 }
141 
142 void JobListView::onJobListModelDataChanged(const QModelIndex& topLeft,
143  const QModelIndex& bottomRight)
144 {
145  // currently only single items change, not ranges; thus ranges are not supported
146  ASSERT(topLeft == bottomRight);
147 
148  if (m_listView->selectionModel()->isSelected(topLeft))
149  updateActions();
150 }
151 
153 {
154  for (const QModelIndex& index : m_listView->selectionModel()->selectedIndexes())
155  m_model->runJob(index);
156 }
157 
159 {
160  for (const QModelIndex& index : m_listView->selectionModel()->selectedIndexes())
161  m_model->cancelJob(index);
162 }
163 
165 {
166  QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
167  std::sort(indexes.begin(), indexes.end(), row_descending);
168  for (const QModelIndex& index : indexes) {
169  m_model->removeJob(index);
170  }
172 }
173 
175 {
176  QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
177 
178  IntensityDataItem* srcData = srcJob->intensityDataItem();
179  if (!srcData)
180  return;
181 
182  for (const QModelIndex& index : indexes) {
183  JobItem* job = m_model->jobForIndex(index);
184  if (job != srcJob) {
185  IntensityDataItem* data = job->intensityDataItem();
186  if (data) {
187  data->setLowerX(srcData->getLowerX());
188  data->setUpperX(srcData->getUpperX());
189  data->setLowerY(srcData->getLowerY());
190  data->setUpperY(srcData->getUpperY());
191  data->setLowerZ(srcData->getLowerZ());
192  data->setUpperZ(srcData->getUpperZ());
193  }
194  }
195  }
196 }
197 
198 void JobListView::showContextMenu(const QPoint&)
199 {
200  QMenu menu(this);
201  menu.addAction(m_runAction);
202  menu.addAction(m_cancelAction);
203  menu.addAction(m_removeAction);
204  menu.addSeparator();
205 
206  m_equalizeMenu->clear();
207  QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
208  if (indexes.size() > 1) {
209  std::sort(indexes.begin(), indexes.end(), row_ascending);
210  for (const QModelIndex& index : indexes) {
211  JobItem* job = m_model->jobForIndex(index);
212  QAction* action = m_equalizeMenu->addAction(QString("to ").append(job->jobName()));
213  connect(action, &QAction::triggered, this, [this, job] { equalizeSelectedToJob(job); });
214  }
215  m_equalizeMenu->setEnabled(true);
216  } else
217  m_equalizeMenu->setEnabled(false);
218  menu.addMenu(m_equalizeMenu);
219  menu.exec(QCursor::pos());
220 }
221 
222 //--------------------------------------------------------------------------------------------------
223 // private member functions
224 //--------------------------------------------------------------------------------------------------
225 
227 {
228  QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
229 
230  struct IsRunningOrFitting {
232  IsRunningOrFitting(JobListModel* model)
233  : m_model(model)
234  {
235  }
236  bool operator()(const QModelIndex& i) const
237  {
238  JobItem* job = m_model->jobForIndex(i);
239  ASSERT(job);
240  return job->isRunning() || job->isFitting();
241  }
242  };
243 
244  bool none_running = std::none_of(indexes.begin(), indexes.end(), IsRunningOrFitting(m_model));
245  bool all_running = std::all_of(indexes.begin(), indexes.end(), IsRunningOrFitting(m_model));
246  bool nonempty = !indexes.empty();
247  m_runAction->setEnabled(nonempty && none_running);
248  m_cancelAction->setEnabled(nonempty && all_running);
249  m_removeAction->setEnabled(nonempty && none_running);
250 }
251 
253 {
254  if (!m_listView->selectionModel()->hasSelection() && m_model->rowCount()) {
255  QModelIndex last = m_model->index(m_model->rowCount() - 1, 0, QModelIndex());
256  m_listView->selectionModel()->select(last, QItemSelectionModel::ClearAndSelect);
257  }
258 }
Defines class IntensityDataItem.
Defines class JobItem.
Defines class JobListModel.
Defines class JobListViewDelegate.
Defines class JobListView.
Defines class JobModel.
Defines class StyledToolbar.
void setLowerX(double value)
double getLowerZ() const
Returns lower and upper zoom ranges of z-axis.
void setLowerY(double value)
void setUpperZ(double zmax)
void setLowerZ(double zmin)
double getLowerY() const
Returns lower and upper zoom ranges of y-axis.
void setUpperX(double value)
double getUpperY() const
double getUpperX() const
void setUpperY(double value)
double getLowerX() const
Returns lower and upper zoom ranges of x-axis.
double getUpperZ() const
QString jobName() const
Definition: JobItem.cpp:84
bool isRunning() const
Definition: JobItem.cpp:129
IntensityDataItem * intensityDataItem()
Definition: JobItem.cpp:96
bool isFitting() const
Definition: JobItem.cpp:149
void removeJob(const QModelIndex &index)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void cancelJob(const QModelIndex &index)
QModelIndex indexForJob(JobItem *job)
JobItem * jobForIndex(const QModelIndex &index) const
void runJob(const QModelIndex &index)
ViewDelegate to show progress bar JobQueuListView.
void equalizeSelectedToJob(JobItem *job)
QAction * m_cancelAction
Definition: JobListView.h:59
void onRemove()
JobListModel * m_model
Definition: JobListView.h:57
QListView * m_listView
Definition: JobListView.h:55
void onJobListModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
void onItemSelectionChanged()
QVector< JobItem * > selectedJobs() const
QAction * m_runAction
Definition: JobListView.h:58
JobListViewDelegate * m_listViewDelegate
Definition: JobListView.h:56
QMenu * m_equalizeMenu
Definition: JobListView.h:61
void ensureItemSelected()
JobListView(JobModel *jobs, QWidget *parent=nullptr, Qt::WindowFlags f=Qt::WindowFlags())
Definition: JobListView.cpp:51
void selectedJobsChanged(const QVector< JobItem * > &jobs)
QAction * m_removeAction
Definition: JobListView.h:60
void showContextMenu(const QPoint &pos)
void onCancel()
void selectJob(JobItem *job)
void updateActions()
The StyledToolbar class represents our standard narrow toolbar with the height 24 pixels.
Definition: StyledToolbar.h:22