Motiv
Marvelous OTF2 Traces Interactive Visualizer
Loading...
Searching...
No Matches
MainWindow.cpp
1/*
2 * Marvelous OTF2 Traces Interactive Visualizer (MOTIV)
3 * Copyright (C) 2023 Florian Gallrein, Björn Gehrke
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include "MainWindow.hpp"
19
20#include <QApplication>
21#include <QCoreApplication>
22#include <QErrorMessage>
23#include <QFileDialog>
24#include <QHBoxLayout>
25#include <QMenuBar>
26#include <QMessageBox>
27#include <QProcess>
28#include <QToolBar>
29#include <utility>
30
31#include "src/models/AppSettings.hpp"
32#include "src/ui/widgets/License.hpp"
33#include "src/ui/widgets/Help.hpp"
34#include "src/ui/widgets/TimeInputField.hpp"
35#include "src/ui/widgets/Timeline.hpp"
36#include "src/ui/TimeUnit.hpp"
37#include "src/ui/windows/FilterPopup.hpp"
38#include "src/ui/widgets/About.hpp"
39#include "src/ui/widgets/TraceOverviewDock.hpp"
40#include "src/ui/widgets/InformationDock.hpp"
41#include "src/ui/widgets/infostrategies/InformationDockSlotStrategy.hpp"
42#include "src/ui/widgets/infostrategies/InformationDockTraceStrategy.hpp"
43#include "src/ui/widgets/infostrategies/InformationDockCommunicationStrategy.hpp"
44#include "src/ui/widgets/infostrategies/InformationDockCollectiveCommunicationStrategy.hpp"
45
46
47MainWindow::MainWindow(QString filepath) : QMainWindow(nullptr), filepath(std::move(filepath)) {
48 if (this->filepath.isEmpty()) {
49 this->promptFile();
50 }
51 this->loadSettings();
52 this->loadTrace();
53
54 this->createToolBars();
55 this->createDockWidgets();
56 this->createCentralWidget();
57 this->createMenus();
58}
59
60MainWindow::~MainWindow() {
61 delete this->data;
62 delete this->callbacks;
63 delete this->reader;
64 delete this->settings;
65
66 delete this->traceOverview;
67 delete this->information;
68
69 delete this->licenseWindow;
70 delete this->helpWindow;
71 delete this->aboutWindow;
72}
73
74void MainWindow::createMenus() {
75 auto menuBar = this->menuBar();
76
78 auto openTraceAction = new QAction(tr("&Open..."), this);
79 openTraceAction->setShortcut(tr("Ctrl+O"));
80 connect(openTraceAction, &QAction::triggered, this, &MainWindow::openNewTrace);
81 auto openRecentMenu = new QMenu(tr("&Open recent"));
82 if (AppSettings::getInstance().recentlyOpenedFiles().isEmpty()) {
83 auto emptyAction = openRecentMenu->addAction(tr("&(Empty)"));
84 emptyAction->setEnabled(false);
85 } else {
86 // TODO this is not updated on call to clear
87 for (const auto &recent: AppSettings::getInstance().recentlyOpenedFiles()) {
88 auto recentAction = new QAction(recent, openRecentMenu);
89 openRecentMenu->addAction(recentAction);
90 connect(recentAction, &QAction::triggered, [&, this] {
91 this->openNewWindow(recent);
92 });
93 }
94 openRecentMenu->addSeparator();
95
96 auto clearRecentMenuAction = new QAction(tr("&Clear history"));
97 openRecentMenu->addAction(clearRecentMenuAction);
98 connect(clearRecentMenuAction, &QAction::triggered, [&] {
100 openRecentMenu->clear();
101 });
102 }
103
104 auto quitAction = new QAction(tr("&Quit"), this);
105 quitAction->setShortcut(tr("Ctrl+Q"));
106 connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));
107
108 auto fileMenu = menuBar->addMenu(tr("&File"));
109 fileMenu->addAction(openTraceAction);
110 fileMenu->addMenu(openRecentMenu);
111 fileMenu->addSeparator();
112 fileMenu->addAction(quitAction);
113
115 auto filterAction = new QAction(tr("&Filter"));
116 filterAction->setShortcut(tr("Ctrl+S"));
117 connect(filterAction, SIGNAL(triggered()), this, SLOT(openFilterPopup()));
118
119 auto searchAction = new QAction(tr("&Find"));
120 searchAction->setShortcut(tr("Ctrl+F"));
121 connect(searchAction, SIGNAL(triggered()), this, SLOT(openFilterPopup()));
122
123 auto resetZoomAction = new QAction(tr("&Reset zoom"));
124 connect(resetZoomAction, SIGNAL(triggered()), this, SLOT(resetZoom()));
125 resetZoomAction->setShortcut(tr("Ctrl+R"));
126
127 auto widgetMenu = new QMenu(tr("Tool Windows"));
128
129 auto showOverviewAction = new QAction(tr("Show &trace overview"));
130 showOverviewAction->setCheckable(true);
131 connect(showOverviewAction, SIGNAL(toggled(bool)), this->traceOverview, SLOT(setVisible(bool)));
132 connect(this->traceOverview, SIGNAL(visibilityChanged(bool)), showOverviewAction, SLOT(setChecked(bool)));
133
134 auto showDetailsAction = new QAction(tr("Show &detail view"));
135 showDetailsAction->setCheckable(true);
136 connect(showDetailsAction, SIGNAL(toggled(bool)), this->information, SLOT(setVisible(bool)));
137 connect(this->information, SIGNAL(visibilityChanged(bool)), showDetailsAction, SLOT(setChecked(bool)));
138
139 widgetMenu->addAction(showOverviewAction);
140 widgetMenu->addAction(showDetailsAction);
141
142 auto viewMenu = menuBar->addMenu(tr("&View"));
143 viewMenu->addAction(filterAction);
144 viewMenu->addAction(searchAction);
145 viewMenu->addAction(resetZoomAction);
146 viewMenu->addMenu(widgetMenu);
147
149 auto minimizeAction = new QAction(tr("&Minimize"));
150 minimizeAction->setShortcut(tr("Ctrl+M"));
151 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(showMinimized()));
152
153 auto windowMenu = menuBar->addMenu(tr("&Window"));
154 windowMenu->addAction(minimizeAction);
155
157 auto showLicenseAction = new QAction(tr("&View license"));
158 connect(showLicenseAction, &QAction::triggered, this, [this] {
159 if(!this->licenseWindow) this->licenseWindow = new License;
160 this->licenseWindow->show();
161 });
162 auto showHelpAction = new QAction(tr("&Show help"));
163 showHelpAction->setShortcut(tr("F1"));
164 connect(showHelpAction, &QAction::triggered, this, [this] {
165 if(!this->helpWindow) this->helpWindow = new Help;
166 this->helpWindow->show();
167 });
168 auto showAboutQtAction = new QAction(tr("&About Qt"));
169 connect(showAboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
170 auto showAboutAction = new QAction(tr("&About"));
171 showAboutAction->setShortcut(tr("Shift+F1"));
172 connect(showAboutAction, &QAction::triggered, this, [this] {
173 if(!this->aboutWindow) this->aboutWindow= new About;
174 this->aboutWindow->show();
175 });
176
177 auto helpMenu = menuBar->addMenu(tr("&Help"));
178 helpMenu->addAction(showLicenseAction);
179 helpMenu->addAction(showHelpAction);
180 helpMenu->addAction(showAboutQtAction);
181 helpMenu->addAction(showAboutAction);
182}
183
184void MainWindow::createToolBars() {
185 // Top toolbar contains preview/control of whole trace
186// this->topToolbar = new QToolBar(this);
187// this->topToolbar->setMovable(false);
188// addToolBar(Qt::TopToolBarArea, this->topToolbar);
189
190 // Bottom toolbar contains control fields
191 this->bottomToolbar = new QToolBar(this);
192 this->bottomToolbar->setMovable(false);
193 this->addToolBar(Qt::BottomToolBarArea, this->bottomToolbar);
194
195 auto bottomContainerWidget = new QWidget(this->bottomToolbar);
196 auto containerLayout = new QHBoxLayout(bottomContainerWidget);
197 bottomContainerWidget->setLayout(containerLayout);
198
199 // TODO populate with initial time stamps
200 this->startTimeInputField = new TimeInputField("Start", TimeUnit::Second, data->getFullTrace()->getStartTime(),
201 bottomContainerWidget);
202 this->startTimeInputField->setUpdateFunction(
203 [this](auto newStartTime) { this->data->setSelectionBegin(newStartTime); });
204 containerLayout->addWidget(this->startTimeInputField);
205 this->endTimeInputField = new TimeInputField("End", TimeUnit::Second, data->getFullTrace()->getEndTime(),
206 bottomContainerWidget);
207 this->endTimeInputField->setUpdateFunction([this](auto newEndTime) { this->data->setSelectionEnd(newEndTime); });
208 containerLayout->addWidget(this->endTimeInputField);
209
210 connect(data, SIGNAL(beginChanged(types::TraceTime)), this->startTimeInputField, SLOT(setTime(types::TraceTime)));
211 connect(data, SIGNAL(endChanged(types::TraceTime)), this->endTimeInputField, SLOT(setTime(types::TraceTime)));
212
213 this->bottomToolbar->addWidget(bottomContainerWidget);
214
215}
216
217void MainWindow::createDockWidgets() {
218 this->information = new InformationDock();
223
224 this->information->setElement(this->data->getFullTrace());
225 // @formatter:off
226 connect(information, SIGNAL(zoomToWindow(types::TraceTime, types::TraceTime)), data,
227 SLOT(setSelection(types::TraceTime, types::TraceTime)));
228
229 connect(data, SIGNAL(infoElementSelected(TimedElement * )), information, SLOT(setElement(TimedElement * )));
230 // @formatter:on
231 this->addDockWidget(Qt::RightDockWidgetArea, this->information);
232
233 this->traceOverview = new TraceOverviewDock(this->data);
234 this->addDockWidget(Qt::TopDockWidgetArea, this->traceOverview);
235}
236
237void MainWindow::createCentralWidget() {
238 auto timeline = new Timeline(data, this);
239 this->setCentralWidget(timeline);
240}
241
242QString MainWindow::promptFile() {
243 auto newFilePath = QFileDialog::getOpenFileName(this, QFileDialog::tr("Open trace"), QString(),
244 QFileDialog::tr("OTF Traces (*.otf *.otf2)"));
245
246 // TODO this is still not really a great way to deal with that, especially for the initial open
247 if (newFilePath.isEmpty()) {
248 auto errorMsg = new QErrorMessage(nullptr);
249 errorMsg->showMessage("The chosen file is invalid!");
250 }
251
252 return newFilePath;
253}
254
255void MainWindow::loadTrace() {
256 this->reader = new otf2::reader::reader(this->filepath.toStdString());
257 this->callbacks = new ReaderCallbacks(*reader);
258
259 this->reader->set_callback(*callbacks);
260 this->reader->read_definitions();
261 this->reader->read_events();
262
263 auto slots = this->callbacks->getSlots();
264 auto communications = this->callbacks->getCommunications();
265 auto collectives = this->callbacks->getCollectiveCommunications();
266 auto trace = new FileTrace(slots, communications, collectives, this->callbacks->duration());
267
268 this->data = new TraceDataProxy(trace, this->settings, this);
269}
270
271void MainWindow::loadSettings() {
272 this->settings = new ViewSettings();
273}
274
276 data->setSelection(types::TraceTime(0), data->getTotalRuntime());
277}
278
280 FilterPopup filterPopup(data->getSettings()->getFilter());
281
282 auto connection = connect(&filterPopup, SIGNAL(filterChanged(Filter)), this->data, SLOT(setFilter(Filter)));
283
284 filterPopup.exec();
285
286 disconnect(connection);
287}
288
290 auto path = this->promptFile();
291 this->openNewWindow(path);
292}
293
294void MainWindow::openNewWindow(QString path) {
295 QProcess::startDetached(
296 QFileInfo(QCoreApplication::applicationFilePath()).absoluteFilePath(),
297 QStringList(path));
298}
299
A widget showcasing information about this software.
Definition: About.hpp:30
static AppSettings & getInstance()
Definition: AppSettings.hpp:36
void recentlyOpenedFilesClear()
Clears the recently opened files list.
Definition: AppSettings.cpp:44
Trace representing the whole trace loaded from trace files.
Definition: Filetrace.hpp:27
A simple popup showing filter options.
Definition: FilterPopup.hpp:32
Class containing options to filter the view.
Definition: Filter.hpp:28
A widget which provides users with information about the various views and controls available in the ...
Definition: Help.hpp:27
A InformationDockElementStrategy to display information about a CollectiveCommunicationEvent.
A InformationDockElementStrategy to display information about a Communication.
A InformationDockElementStrategy to display information about a Slot.
A InformationDockElementStrategy to display information about a Trace.
A UI component that shows additional information.
void addElementStrategy(InformationDockElementStrategy *s)
Add a strategy to display information for specific items.
void setElement(TimedElement *element)
Sets the current element.
A widget showcasing displaying license information about this software.
Definition: License.hpp:27
MainWindow(QString filepath=QString())
Creates a new instance of the MainWindow class.
Definition: MainWindow.cpp:47
void resetZoom()
Resets the zoom to show the entire trace.
Definition: MainWindow.cpp:275
void openNewTrace()
Asks for a new trace file and opens the trace.
Definition: MainWindow.cpp:289
void openFilterPopup()
Opens and shows the FilterPopup.
Definition: MainWindow.cpp:279
Class implementing handlers for the otf readers events.
std::vector< Slot * > getSlots()
Returns all read slots.
std::vector< Communication * > getCommunications()
Returns all read point to point communications.
otf2::chrono::duration duration() const
std::vector< CollectiveCommunicationEvent * > getCollectiveCommunications()
Returns all read collective communications.
A widget for entering a time point.
void setUpdateFunction(std::function< void(types::TraceTime)>)
A base class for all elements with a start and end time.
virtual types::TraceTime getEndTime() const =0
Returns the end time of the current object.
virtual types::TraceTime getStartTime() const =0
Returns the start time of the current object.
Container widget for actual view and axis labels.
Definition: Timeline.hpp:35
Model class providing access to data and pub/sub architecture of change events.
ViewSettings * getSettings() const
Returns the current view settings.
types::TraceTime getTotalRuntime() const
void setSelectionBegin(types::TraceTime newBegin)
Trace * getFullTrace() const
Returns the entire trace.
void setSelectionEnd(types::TraceTime newEnd)
void setSelection(types::TraceTime newBegin, types::TraceTime newEnd)
A DockWidget holding a TraceOverviewTimelineView.
The ViewSettings class encapsulates settings for the main view.
Filter getFilter() const
Returns the current filter.