BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
MainWindow.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Main/MainWindow.cpp
6 //! @brief Implements class MainWindow
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 
19 #include "GUI/Util/Path.h"
22 #include "GUI/View/Job/JobView.h"
31 #include <QAction>
32 #include <QApplication>
33 #include <QBoxLayout>
34 #include <QButtonGroup>
35 #include <QCloseEvent>
36 #include <QMessageBox>
37 #include <QProgressBar>
38 #include <QPushButton>
39 #include <QSettings>
40 #include <QStackedLayout>
41 #include <QStatusBar>
42 #include <QToolButton>
43 
45  : QMainWindow(nullptr)
46  , m_progressBar(new QProgressBar)
47  , m_viewSelectionButtons(new QButtonGroup(this))
48  , m_viewsStack(new QStackedLayout)
49  , m_viewSelectionButtonsLayout(new QVBoxLayout)
50  , m_projectManager(new ProjectManager(this))
51  , m_actionManager(new ActionManager(this))
52  , m_welcomeView(nullptr)
53  , m_instrumentView(nullptr)
54  , m_sampleView(nullptr)
55  , m_importDataView(nullptr)
56  , m_simulationView(nullptr)
57  , m_projectSettingsView(nullptr)
58  , m_jobView(nullptr)
59  , m_sessionModelView(nullptr)
60 {
61  auto* centralWidget = new QWidget(this);
62  auto* mainLayout = new QHBoxLayout(centralWidget);
63  mainLayout->setMargin(0);
64  mainLayout->setSpacing(0);
65 
66  m_viewSelectionButtonsLayout->setMargin(0);
67  m_viewSelectionButtonsLayout->setSpacing(0);
68 
69  auto* fillerButton = createViewSelectionButton();
70  fillerButton->setMinimumSize(5, 5);
71  fillerButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
72  fillerButton->setEnabled(false);
73  m_viewSelectionButtonsLayout->insertWidget(-1, fillerButton);
74 
75 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
76  connect(m_viewSelectionButtons, &QButtonGroup::idClicked, this, &MainWindow::raiseView);
77 #else
78  connect(m_viewSelectionButtons, QOverload<int>::of(&QButtonGroup::buttonClicked), this,
80 #endif
81 
82  auto* vlayout = new QVBoxLayout;
83  vlayout->setMargin(0);
84  vlayout->setSpacing(0);
85  vlayout->addLayout(m_viewsStack);
86 
87  mainLayout->addLayout(m_viewSelectionButtonsLayout);
88  mainLayout->addLayout(vlayout);
89 
90  setCentralWidget(centralWidget);
91 
93  readSettings();
95  initViews();
96 
99 
102 
105 
106  ASSERT(m_viewSelectionButtons->button(ViewId::WELCOME) != nullptr);
107  m_viewSelectionButtons->button(ViewId::WELCOME)->setChecked(true);
108 
109  updateTitle();
110 
113 }
114 
115 MainWindow::~MainWindow() = default;
116 
117 QProgressBar* MainWindow::progressBar()
118 {
119  return m_progressBar;
120 }
121 
123 {
124  return m_projectManager;
125 }
126 
127 QWidget* MainWindow::currentView() const
128 {
129  return m_viewsStack->currentWidget();
130 }
131 
133 {
134  if (auto* btn = m_viewSelectionButtons->button(viewId); btn != nullptr)
135  btn->click();
136 }
137 
138 void MainWindow::raiseView(int viewId)
139 {
140  if (m_viewsStack->currentIndex() != viewId) {
141  m_viewsStack->setCurrentIndex(viewId);
142  emit currentViewChanged(ViewId(viewId));
143  }
144 }
145 
147 {
148  auto const doc = gSessionData->projectDocument;
149  if (!doc.has_value())
150  setWindowTitle("BornAgain");
151  else if (doc.value()->isModified())
152  setWindowTitle("BornAgain - *" + doc.value()->projectName());
153  else
154  setWindowTitle("BornAgain - " + doc.value()->projectName());
155 }
156 
158 {
159  m_viewSelectionButtons->button(index)->click();
160 }
161 
163 {
164  if (const auto* action = qobject_cast<const QAction*>(sender())) {
165  auto file = action->data().value<QString>();
167  }
168 }
169 
171 {
172  // This clearFocus is needed for the propagation of the current editor value,
173  // since the simulate method will only change focus after finishing the simulation
174  if (auto* widget = QApplication::focusWidget())
175  widget->clearFocus();
177 }
178 
179 //! Inserts/removes developers SessionModelView on the left tabbar.
181 {
182  auto* btn = m_viewSelectionButtons->button(ViewId::SESSIONMODEL);
183  ASSERT(btn != nullptr);
184  if (btn == nullptr)
185  return;
186 
187  if (!isActive && m_viewsStack->currentIndex() == SESSIONMODEL)
188  m_viewSelectionButtons->buttons().first()->click();
189 
190  btn->setEnabled(isActive);
191  btn->setVisible(isActive);
192 }
193 
194 void MainWindow::closeEvent(QCloseEvent* event)
195 {
196  if (gSessionData->projectDocument.has_value()
197  && gSessionData->projectDocument.value()->jobModel()->hasUnfinishedJobs()) {
198  QMessageBox::warning(this, "Can't quit the application.",
199  "Can't quit the application while jobs are running.\n"
200  "Cancel running jobs or wait until they are completed.");
201  event->ignore();
202  return;
203  }
205  writeSettings();
206  event->accept();
207  } else {
208  event->ignore();
209  }
210 }
211 
213 {
214  setDockNestingEnabled(true);
215  setAcceptDrops(true);
216 
217  setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
218  setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
219 }
220 
222 {
223  m_progressBar->hide();
224  m_progressBar->setTextVisible(false);
225  m_progressBar->setFixedHeight(QApplication::fontMetrics().boundingRect("M").height());
226  m_progressBar->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
228 }
229 
231 {
232  if (m_welcomeView == nullptr) {
233  m_welcomeView = new WelcomeView(this);
234  addView(ViewId::WELCOME, QIcon(":/images/main_welcomeview.svg"), "Welcome",
235  "Switch to Welcome View", m_welcomeView);
236  }
237 
238  if (gSessionData->projectDocument.has_value()) {
239  auto* doc = gSessionData->projectDocument.value();
240  m_instrumentView = new InstrumentView(this, doc);
241  m_sampleView = new SampleView(this, doc);
242  m_importDataView = new ImportDataView(this, doc);
243  m_simulationView = new SimulationView(this, doc);
244  m_jobView = new JobView(this, doc);
245  m_sessionModelView = new SessionModelView(this, doc);
247 
248  addView(ViewId::PROJECT, QIcon(":/images/main_sessionmodel.svg"), "Project",
249  "Define project settings", m_projectSettingsView);
250 
251  addView(ViewId::INSTRUMENT, QIcon(":/images/main_instrumentview.svg"), "Instrument",
252  "Define the beam and the detector", m_instrumentView);
253 
254  addView(ViewId::SAMPLE, QIcon(":/images/main_sampleview.svg"), "Sample", "Build the sample",
255  m_sampleView);
256 
257  addView(ViewId::IMPORT, QIcon(":/images/main_importview.svg"), "Data",
258  "Import intensity data to fit", m_importDataView);
259 
260  addView(ViewId::SIMULATION, QIcon(":/images/main_simulationview.svg"), "Simulation",
261  "Run simulation", m_simulationView);
262 
263  addView(ViewId::JOB, QIcon(":/images/main_jobview.svg"), "Jobs",
264  "Switch to see job results, tune parameters real time,\nfit the data", m_jobView);
265 
266  addView(ViewId::SESSIONMODEL, QIcon(":/images/main_sessionmodel.svg"), "Models", "",
268 
269  // enabling technical view
270  QSettings settings;
271  settings.beginGroup(GUI::Constants::S_SESSIONMODELVIEW);
272  onSessionModelViewActive(settings.value(GUI::Constants::S_VIEWISACTIVE, false).toBool());
273  settings.endGroup();
274 
276  }
277 }
278 
280 {
281  QSettings settings;
282  if (settings.childGroups().contains(GUI::Constants::S_MAINWINDOW)) {
283  settings.beginGroup(GUI::Constants::S_MAINWINDOW);
284  resize(settings.value(GUI::Constants::S_WINDOWSIZE, QSize(400, 400)).toSize());
285  move(settings.value(GUI::Constants::S_WINDOWPOSITION, QPoint(200, 200)).toPoint());
286  settings.endGroup();
287  }
289 }
290 
292 {
293  QSettings settings;
294  settings.beginGroup(GUI::Constants::S_MAINWINDOW);
295  settings.setValue(GUI::Constants::S_WINDOWSIZE, size());
296  settings.setValue(GUI::Constants::S_WINDOWPOSITION, pos());
297  settings.endGroup();
299  settings.sync();
300 }
301 
302 void MainWindow::addView(ViewId id, const QIcon& icon, const QString& title, const QString& tooltip,
303  QWidget* view)
304 {
305  QToolButton* btn = createViewSelectionButton();
306  m_viewSelectionButtonsLayout->insertWidget(id, btn);
307 
308  btn->setText(title);
309  btn->setToolTip(tooltip);
310  btn->setIcon(icon);
311  m_viewSelectionButtons->addButton(btn, id);
312 
314 
315  m_viewsStack->insertWidget(id, view);
316 }
317 
319 {
320  if (m_viewSelectionButtons->buttons().isEmpty())
321  return;
322 
323  const QFontMetrics fontMetrics = m_viewSelectionButtons->buttons().first()->fontMetrics();
324 
325  // Find the maximum text extents
326  int maxTextWidth = 0;
327  int maxTextHeight = 0;
328  for (auto* b : m_viewSelectionButtons->buttons()) {
329  const auto r = fontMetrics.boundingRect(b->text());
330  maxTextWidth = std::max(maxTextWidth, r.width());
331  maxTextHeight = std::max(maxTextHeight, r.height());
332  }
333 
334  // calculate the button extent by width (width == height!). Ensure an extent of 70 for normal
335  // DPI devices (legacy value)
336  const int margin = fontMetrics.boundingRect("M").width();
337  const int buttonExtent = std::max(70, maxTextWidth + 2 * margin);
338 
339  // calculate the icon extent by height (width == height!)
340  const int iconExtent = buttonExtent - margin - maxTextHeight;
341 
342  // set new values in all buttons
343  for (auto* b : m_viewSelectionButtons->buttons()) {
344  b->setFixedSize(buttonExtent, buttonExtent);
345  b->setIconSize({iconExtent, iconExtent});
346  }
347  // set fixed width in filler and progress bar
348  auto* filler = m_viewSelectionButtonsLayout->itemAt(m_viewSelectionButtons->buttons().size());
349  if (filler)
350  if (auto* fillerBtn = dynamic_cast<QToolButton*>(filler->widget()); fillerBtn)
351  fillerBtn->setFixedWidth(buttonExtent);
352 
353  m_progressBar->setFixedWidth(buttonExtent);
354 }
355 
357 {
358  initViews();
359  updateTitle();
360  if (open)
362 }
363 
365 {
366  updateTitle();
367 }
368 
370 {
372 
373  while (m_viewSelectionButtons->buttons().size() > 1)
374  delete m_viewSelectionButtons->buttons().last();
375 
377 
378  delete m_instrumentView;
379  m_instrumentView = nullptr;
380 
381  delete m_sampleView;
382  m_sampleView = nullptr;
383 
384  delete m_importDataView;
385  m_importDataView = nullptr;
386 
387  delete m_simulationView;
388  m_simulationView = nullptr;
389 
390  delete m_jobView;
391  m_jobView = nullptr;
392 
393  delete m_sessionModelView;
394  m_sessionModelView = nullptr;
395 }
396 
398 {
399  auto* btn = new QToolButton;
400  btn->setObjectName("ViewSelectionButton"); // for addressing in style sheet
401  btn->setCheckable(true);
402  btn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
403  return btn;
404 }
Defines class ActionManager.
ApplicationSettings * appSettings
global pointer to the instance
Defines class ApplicationSettings.
Defines class ImportDataView.
Defines class InstrumentView.
Defines class JobModel.
Defines class JobView.
Defines class MainWindow.
Defines class Helpers functions.
Defines class ProjectManager.
Defines class ProjectSettingsView.
Defines class SampleView.
SessionData * gSessionData
global pointer to the single instance
Definition: SessionData.cpp:17
Defines struct SessionData.
Defines class SessionModelView.
Defines class SimulationView.
Defines class WelcomeView.
Class to handle MainWindow's menu and corresponding actions.
Definition: ActionManager.h:28
bool createNewProjectOnStartup() const
The ImportDataView class is a main view for importing experimental data.
void focusRequest(int)
void closeEvent(QCloseEvent *event) override
Definition: MainWindow.cpp:194
void readSettings()
Definition: MainWindow.cpp:279
SimulationView * m_simulationView
Definition: MainWindow.h:102
void raiseView(int viewId)
Definition: MainWindow.cpp:138
@ SESSIONMODEL
Definition: MainWindow.h:43
WelcomeView * m_welcomeView
Definition: MainWindow.h:98
SessionModelView * m_sessionModelView
Definition: MainWindow.h:105
void setCurrentView(int viewId)
Definition: MainWindow.cpp:132
~MainWindow() override
void onDocumentOpenedOrClosed(bool open)
Definition: MainWindow.cpp:356
QButtonGroup * m_viewSelectionButtons
Definition: MainWindow.h:90
InstrumentView * m_instrumentView
Definition: MainWindow.h:99
QWidget * currentView() const
Definition: MainWindow.cpp:127
void writeSettings()
Definition: MainWindow.cpp:291
void onSessionModelViewActive(bool isActive)
Inserts/removes developers SessionModelView on the left tabbar.
Definition: MainWindow.cpp:180
ProjectManager * m_projectManager
Definition: MainWindow.h:94
ImportDataView * m_importDataView
Definition: MainWindow.h:101
void onFocusRequest(int index)
Definition: MainWindow.cpp:157
void addView(ViewId id, const QIcon &icon, const QString &title, const QString &tooltip, QWidget *view)
Definition: MainWindow.cpp:302
void onRunSimulationShortcut()
Definition: MainWindow.cpp:170
QStackedLayout * m_viewsStack
Definition: MainWindow.h:91
void openRecentProject()
Definition: MainWindow.cpp:162
void initViews()
Definition: MainWindow.cpp:230
void initProgressBar()
Definition: MainWindow.cpp:221
QProgressBar * progressBar()
Definition: MainWindow.cpp:117
QVBoxLayout * m_viewSelectionButtonsLayout
Definition: MainWindow.h:92
QProgressBar * m_progressBar
Definition: MainWindow.h:89
void updateTitle()
Definition: MainWindow.cpp:146
void currentViewChanged(ViewId newView)
void onAboutToCloseDocument()
Definition: MainWindow.cpp:369
void onDocumentModified()
Definition: MainWindow.cpp:364
ProjectManager * projectManager()
Definition: MainWindow.cpp:122
void initApplication()
Definition: MainWindow.cpp:212
ProjectSettingsView * m_projectSettingsView
Definition: MainWindow.h:103
QToolButton * createViewSelectionButton() const
Definition: MainWindow.cpp:397
JobView * m_jobView
Definition: MainWindow.h:104
void updateViewSelectionButtonsGeometry() const
Recalculate the size of the view selection buttons to show complete button text.
Definition: MainWindow.cpp:318
SampleView * m_sampleView
Definition: MainWindow.h:100
Handles activity related to opening/save projects.
void aboutToCloseDocument()
void documentModified()
bool closeCurrentProject()
Processes close current project request. Call save/discard/cancel dialog, if necessary....
void readSettings()
Reads settings of ProjectManager from global settings.
void openProject(QString fileName="")
Opens existing project. If fileName is empty, will popup file selection dialog.
void newProject()
Processes new project request (close old project, rise dialog for project name, create project).
void documentOpenedOrClosed(bool opened)
void writeSettings()
Saves settings of ProjectManager in global settings.
Widget to define project settings.
The SessionModelView is a technical view which shows the content all current application models....
Widget to define a simulation. Contains:
Defines namespace GUI::Constants.
const char S_VIEWISACTIVE[]
const char S_MAINWINDOW[]
const char S_SESSIONMODELVIEW[]
const char S_WINDOWSIZE[]
const char S_WINDOWPOSITION[]
@ SAMPLE
Definition: ID.h:21
@ WELCOME
Definition: ID.h:21
@ SIMULATION
Definition: ID.h:21
@ PROJECT
Definition: ID.h:21
@ JOB
Definition: ID.h:21
@ SESSIONMODEL
Definition: ID.h:21
@ INSTRUMENT
Definition: ID.h:21
@ IMPORT
Definition: ID.h:21
void warning(QWidget *parent, const QString &title, const QString &text, const QString &detailedText)
Definition: MessageBox.cpp:37
std::optional< ProjectDocument * > projectDocument
Definition: SessionData.h:27