BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
SampleListView.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/SampleDesigner/SampleListView.cpp
6 //! @brief Implements class SampleListView
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2021
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Base/Util/SysUtils.h"
24 #include <QAction>
25 #include <QMenu>
26 #include <QPainter>
27 
28 namespace {
29 
30 class ItemDelegateForSampleTree : public ItemDelegateForHTML {
31 public:
32  ItemDelegateForSampleTree(QObject* parent)
33  : ItemDelegateForHTML(parent)
34  {
35  }
36 
37 protected:
38  void paint(QPainter* painter, const QStyleOptionViewItem& option,
39  const QModelIndex& index) const override
40  {
41  ItemDelegateForHTML::paint(painter, option, index);
42 
43  QStyleOptionViewItem options = option;
44  initStyleOption(&options, index);
45 
46  painter->save();
47 
48  painter->setPen(QPen(Qt::lightGray, 1));
49  painter->drawLine(options.rect.left(), options.rect.bottom(), options.rect.right(),
50  options.rect.bottom());
51  painter->restore();
52  }
53 
54  QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override
55  {
56  auto s = ItemDelegateForHTML::sizeHint(option, index);
57  s.setHeight(std::max(s.height(), 32));
58  return s;
59  }
60 };
61 
62 } // namespace
63 
65  : QListView(parent)
66  , m_document(document)
67 {
68  m_model = new SampleListModel(this, document->sampleItems());
69 
70  setContextMenuPolicy(Qt::CustomContextMenu);
71  setModel(m_model);
72  setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
73 
75  this, [=](const QModelIndex& i, bool h) { return getOverlayActions(i, h); });
76  setItemDelegate(new ItemDelegateForSampleTree(this));
77 
78  connect(selectionModel(), &QItemSelectionModel::currentChanged, this,
80 
81  connect(this, &QWidget::customContextMenuRequested, this, &SampleListView::showContextMenu);
82 
83  m_newSampleAction = new QAction(this);
84  m_newSampleAction->setText("Create new sample");
85  m_newSampleAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
86  m_newSampleAction->setIconText("New");
87  m_newSampleAction->setToolTip("Create new sample");
88  connect(m_newSampleAction, &QAction::triggered, this, &SampleListView::createNewSample);
89 
90  m_importSampleAction = new QAction(this);
91  m_importSampleAction->setText("Import sample from python script (experimental)");
92  m_importSampleAction->setIcon(QIcon(":/images/import.svg"));
93  m_importSampleAction->setIconText("Import");
94  m_importSampleAction->setToolTip(
95  "Import sample from Python script.\n The script should contain a function "
96  "returning a valid multi-layer.");
97 
98 #ifdef Q_OS_MAC
99  if (BaseUtils::System::getenv("PYTHONHOME").empty())
100  m_importSampleAction->setEnabled(false);
101 #endif
102 
103 #ifdef BORNAGAIN_PYTHON
104  connect(m_importSampleAction, &QAction::triggered, this,
106 #else
107  m_importSampleAction->setVisible(false);
108 #endif
109 
110  m_chooseFromLibraryAction = new QAction(this);
111  m_chooseFromLibraryAction->setText("Choose from sample examples");
112  m_chooseFromLibraryAction->setIcon(QIcon(":/images/library.svg"));
113  m_chooseFromLibraryAction->setIconText("Examples");
114  m_chooseFromLibraryAction->setToolTip("Choose from sample examples");
115 
116  auto* menu = new QMenu(this);
117  m_chooseFromLibraryAction->setMenu(menu);
118 
119  for (const auto& exampleName : GUI::ExamplesFactory::exampleNames()) {
120  QString title, description;
121  std::tie(title, description) = GUI::ExamplesFactory::exampleInfo(exampleName);
122  auto icon = QIcon(":/SampleDesignerToolbox/images/sample_layers2.png");
123  auto* action = menu->addAction(icon, title);
124  action->setToolTip(description);
125  connect(action, &QAction::triggered,
126  [=]() { createSampleFromLibrary(exampleName, title, description); });
127  }
128 }
129 
131 {
132  setCurrentIndex(m_model->indexForItem(sample));
133 }
134 
136 {
137  return m_model->itemForIndex(currentIndex());
138 }
139 
141 {
142  if (m_document->sampleItems()->sampleItems().isEmpty())
143  return;
145 }
146 
148 {
149  return m_newSampleAction;
150 }
151 
153 {
154  return m_importSampleAction;
155 }
156 
158 {
160 }
161 
163 {
164  QSize s = QListView::sizeHint();
165  s.setWidth(std::max(300, s.width()));
166  return s;
167 }
168 
170 {
171  // if length of description changes height of TreeItem, the position of the overlay buttons
172  // has to be updated -> schedule a re-layout
173  scheduleDelayedItemsLayout();
174 }
175 
177 {
178  const QModelIndex newIndex = m_model->createSample();
179  setCurrentIndex(newIndex);
181 }
182 
183 void SampleListView::createSampleFromLibrary(const QString& classname, const QString& title,
184  const QString& description)
185 {
186  const QModelIndex newIndex = m_model->createSampleFromExamples(classname, title, description);
187  if (newIndex.isValid()) {
188  setCurrentIndex(newIndex);
190  }
191 }
192 
194 {
195 #ifdef BORNAGAIN_PYTHON
196  const QModelIndex newIndex = m_model->createSampleFromPython();
197  if (newIndex.isValid()) {
198  setCurrentIndex(newIndex);
200  }
201 #endif
202 }
203 
204 QList<QAction*> SampleListView::getOverlayActions(const QModelIndex& index, bool asHover)
205 {
206  if (!asHover)
207  return {};
208 
209  auto* item = m_model->itemForIndex(index);
210  if (item == nullptr)
211  return {};
212 
213  return {createRemoveAction(this, item)};
214 }
215 
216 void SampleListView::onCurrentChanged(const QModelIndex& index)
217 {
219 }
220 
222 {
223  m_model->removeSample(item);
225 }
226 
227 QAction* SampleListView::createRemoveAction(QObject* parent, MultiLayerItem* item)
228 {
229  auto* removeAction = new QAction(parent);
230  removeAction->setText("Remove");
231  removeAction->setIcon(QIcon(":/images/delete.svg"));
232  removeAction->setIconText("Remove");
233  removeAction->setToolTip("Remove this sample");
234  connect(removeAction, &QAction::triggered, [=]() { removeSample(item); });
235 
236  return removeAction;
237 }
238 
239 void SampleListView::showContextMenu(const QPoint& pos)
240 {
241  auto* sampleAtPoint = m_model->itemForIndex(indexAt(pos));
242 
243  QMenu menu;
244  menu.setToolTipsVisible(true);
245 
246  if (sampleAtPoint != nullptr) {
247  menu.addAction(createRemoveAction(&menu, sampleAtPoint));
248  menu.addSeparator();
249  }
250 
251  menu.addAction(m_newSampleAction);
252  menu.addAction(m_chooseFromLibraryAction);
253  menu.exec(mapToGlobal(pos));
254 }
Defines class ApplicationSettings.
Defines class GUI::ExamplesFactory.
Defines class ItemDelegateForHTML.
Defines class ItemViewOverlayButtons.
Defines class MultiLayerItem.
Defines class ProjectDocument.
Defines class SampleListModel.
Defines class SampleListView.
For representing HTML text in an item *‍/.
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
static void install(QAbstractItemView *view, FnGetActions fnGetActions)
QVector< MultiLayerItem * > sampleItems() const
Project document class handles all data related to the opened project (sample, job,...
MultiLayerItems * sampleItems()
List model for sample selection (used in the left pane of the layer oriented sample editor)
QModelIndex createSample()
Create a new sample (sample) and return the index of it.
QModelIndex createSampleFromPython()
Create sample from an imported python code.
void removeSample(MultiLayerItem *item)
Remove the given sample. nullptr is allowed.
MultiLayerItem * itemForIndex(const QModelIndex &index) const
QModelIndex createSampleFromExamples(const QString &className, const QString &title, const QString &description)
Create sample from list of built-in examples.
QModelIndex indexForItem(MultiLayerItem *item) const
MultiLayerItem * currentSample()
QAction * m_chooseFromLibraryAction
QAction * chooseFromLibraryAction()
ProjectDocument * m_document
QSize sizeHint() const override
SampleListModel * m_model
QList< QAction * > getOverlayActions(const QModelIndex &index, bool asHover)
void setCurrentSample(MultiLayerItem *sample)
QAction * createRemoveAction(QObject *parent, MultiLayerItem *item)
QAction * importSampleAction()
void currentSampleChanged(MultiLayerItem *current)
void selectFirstSample()
Select first sample, if any sample exists.
QAction * newSampleAction()
void onCurrentChanged(const QModelIndex &index)
void showContextMenu(const QPoint &pos)
QAction * m_newSampleAction
void createSampleFromLibrary(const QString &classname, const QString &title, const QString &description)
void importSampleFromPython()
QAction * m_importSampleAction
void removeSample(MultiLayerItem *item)
SampleListView(QWidget *parent, ProjectDocument *document)
QStringList exampleNames()
The internal example name, e.g. for creation with itemizeSample.
std::tuple< QString, QString > exampleInfo(const QString &name)
Returns human readable name and description.