main_window.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2020,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  *
22  * Authors: Christoph Rösmann
23  *********************************************************************/
24 
25 #include <corbo-gui/main_window.h>
26 
27 #include <QCoreApplication>
28 #include <QDir>
29 #include <QInputDialog>
30 #include <QMenuBar>
31 #include <QMessageBox>
32 #include <QScrollArea>
33 #include <QSettings>
34 #include <QStatusBar>
35 #include <QTimer>
36 #include <QVBoxLayout>
37 
38 #include <memory>
39 #include <utility>
40 
41 namespace corbo {
42 namespace gui {
43 
44 corboMainWindow::corboMainWindow(QWidget* parent)
45 {
46  // set working path of qdir to path of the executable:
47  QDir::setCurrent(QCoreApplication::applicationDirPath());
48 
49  _central_widget = new QWidget;
50  setCentralWidget(_central_widget);
51 
52  setWindowTitle(tr("Control-Box RST"));
53 
54  // resize(800,500);
55  setWindowState(Qt::WindowMaximized);
56 
57  // TODO(roesmann): this should be handled in a separate thread with low priority
58  // create signal helper which stores all available signals
59  _signal_helper = std::make_shared<SignalHelper>();
60 
61  _rpc_client = std::make_shared<MasterServiceClient>();
62 
64  createActions();
65  createMenus();
67 
69 
71 
72  // read window settings from previous runs if available
74 
76 
77  // register meta types for queued signal-slot connections
78  qRegisterMetaType<std::shared_ptr<MasterServiceClient>>("std::shared_ptr<MasterServiceClient>");
79 }
80 
81 corboMainWindow::corboMainWindow(std::shared_ptr<MasterServiceClient> rpc_client, QWidget* parent) : corboMainWindow(parent)
82 {
83  if (_rpc_client) _rpc_client = rpc_client;
84 }
85 
86 corboMainWindow::~corboMainWindow()
87 {
88  _rpc_worker_thread.quit();
89  _rpc_worker_thread.wait();
90 }
91 
92 void corboMainWindow::closeEvent(QCloseEvent* event) { writeWindowSettings(); }
93 
94 void corboMainWindow::createActions()
95 {
96  // save parameters to file
97  _action_save_parameters_file = new QAction(tr("Save Parameters..."), this);
98  _action_save_parameters_file->setStatusTip(tr("Save current parameters to file."));
99  connect(_action_save_parameters_file, &QAction::triggered, _toolbox_widget,
100  [this](bool /*checked*/) { _toolbox_widget->saveCurrentParametersToFile(); });
101 
102  // load parameters from file
103  _action_load_parameters_file = new QAction(tr("Load Parameters..."), this);
104  _action_load_parameters_file->setStatusTip(tr("Load parameters from file."));
105  connect(_action_load_parameters_file, &QAction::triggered, _toolbox_widget,
106  [this](bool /*checked*/) { _toolbox_widget->loadParametersFromFile(); });
107 
108  // Clear scopes
109  _action_remove_signals = new QAction(tr("Clear Signals"), this);
110  _action_remove_signals->setStatusTip(tr("Clear all signals"));
111  connect(_action_remove_signals, &QAction::triggered, [this](bool /*checked*/) {
112  _signal_helper->clearAll();
113  _toolbox_widget->getAvailableSignals(); // TODO(roesmann) should we better clear only recent and not current signals?
114  });
115 
116  // Close all scopes
117  _action_close_scopes = new QAction(tr("Close Scopes"), this);
118  _action_close_scopes->setStatusTip(tr("Clear all scopes"));
119  connect(_action_close_scopes, SIGNAL(triggered()), _scope_collection_widget, SLOT(closeAllScopes()));
120 
121  // rescale scope axes
122  _action_rescale_scope_axes = new QAction(tr("Rescale Scope Axes"), this);
123  connect(_action_rescale_scope_axes, &QAction::triggered, _scope_collection_widget, &ScopeCollectionWidget::resizeScopeAxes);
124 
125  // Reset layout to default
126  _action_default_layout = new QAction(tr("Default Layout"), this);
127  _action_default_layout->setStatusTip(tr("Set docks to its default position"));
128  connect(_action_default_layout, SIGNAL(triggered()), this, SLOT(setDefaultLayout()));
129 
130  // rpc server address
131  _action_rpc_server_address = new QAction(tr("Change Server Address..."), this);
132  connect(_action_rpc_server_address, &QAction::triggered, this, [this](bool /*checked*/) {
133  bool ok;
134  QString text = QInputDialog::getText(this, tr("QInputDialog::getText()"), tr("Server Address:"), QLineEdit::Normal, _rpc_server_address, &ok);
135  if (ok && !text.isEmpty())
136  {
137  _rpc_server_address = text;
138  emit requestRpcServerAddress(_rpc_server_address);
139  }
140  });
141 
142  // About dialog
143  _action_about = new QAction(tr("About"), this);
144  _action_about->setStatusTip(tr("Display general information about the program"));
145  connect(_action_about, SIGNAL(triggered()), this, SLOT(aboutPopup()));
146 
147  // Exit action
148  _action_exit_program = new QAction(tr("Exit"), this);
149  connect(_action_exit_program, SIGNAL(triggered()), this, SLOT(close()));
150 
151  // Recent files
152  for (std::size_t i = 0; i < _action_recent_files.size(); ++i)
153  {
154  _action_recent_files[i] = new QAction(this);
155  _action_recent_files[i]->setVisible(false);
156  QAction* cur_action = _action_recent_files[i];
157  connect(_action_recent_files[i], &QAction::triggered, _toolbox_widget, [this, cur_action]() {
158  if (cur_action) _toolbox_widget->loadParametersFromFile(cur_action->data().toString());
159  });
160  }
161 }
162 
163 void corboMainWindow::createMenus()
164 {
165  // File menu
166  _menu_file = menuBar()->addMenu(tr("File"));
167 
168  // Param menu
169  _menu_parameters = menuBar()->addMenu(tr("Parameters"));
170 
171  // Scopes menu
172  _menu_scopes = menuBar()->addMenu(tr("Scopes"));
173 
174  // Signals menu
175  _menu_signals = menuBar()->addMenu(tr("Signals"));
176 
177  // View menu
178  _menu_view = menuBar()->addMenu(tr("View"));
179 
180  // Help menu
181  _menu_help = menuBar()->addMenu(tr("&Help"));
182 
183 #ifdef __linux__
184  menuBar()->setNativeMenuBar(false); // Otherwise we do not see a menu bar on Ubuntu 14.04
185 #endif
186 }
187 
188 void corboMainWindow::createStatusBar() { displayStatusReady(); }
189 
190 void corboMainWindow::createDockWindows()
191 {
192  // Toolbox dock
193  QScrollArea* toolbox_scroll_area = new QScrollArea;
194  _toolbox_widget = new ToolboxWidget(_signal_helper, _rpc_client);
195  toolbox_scroll_area->setWidget(_toolbox_widget);
196  connect(_toolbox_widget, &ToolboxWidget::parameterSavedToFile, this, &corboMainWindow::addToRecentFileAction);
197  connect(_toolbox_widget, &ToolboxWidget::parameterLoadedFromFile, this, &corboMainWindow::addToRecentFileAction);
198 
199  _dock_toolbox_widget = new QDockWidget(tr("Toolbox"), this);
200  _dock_toolbox_widget->setFloating(false);
201  _dock_toolbox_widget->setObjectName("ToolboxDock");
202  _dock_toolbox_widget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
203  _dock_toolbox_widget->setWidget(_toolbox_widget);
204  _dock_toolbox_widget->setFeatures(QDockWidget::AllDockWidgetFeatures);
205  addDockWidget(Qt::LeftDockWidgetArea, _dock_toolbox_widget);
206 
207  // Scope dock
208  // QScrollArea* scope_scroll_area = new QScrollArea;
209  _scope_collection_widget = new ScopeCollectionWidget(_signal_helper);
210  // scope_scroll_area->setWidget(_scope_collection_widget);
211  connect(_signal_helper.get(), &SignalHelper::newMeasurement, _scope_collection_widget, &ScopeCollectionWidget::addMeasurement);
212  connect(_signal_helper.get(), &SignalHelper::signalRemoved, _scope_collection_widget, &ScopeCollectionWidget::removeSignal);
213  connect(_signal_helper.get(), &SignalHelper::newSeries, _scope_collection_widget, &ScopeCollectionWidget::initializeTask);
214 
215  _dock_scope_widget = new QDockWidget(tr("Scopes"), this);
216  _dock_scope_widget->setFloating(false);
217  _dock_scope_widget->setObjectName("ScopesDock");
218  _dock_scope_widget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
219  // _scope_collection_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
220  // _dock_scope_widget->setWidget(scope_scroll_area);
221  _dock_scope_widget->setWidget(_scope_collection_widget);
222  _dock_scope_widget->setFeatures(QDockWidget::AllDockWidgetFeatures);
223  addDockWidget(Qt::RightDockWidgetArea, _dock_scope_widget);
224 
225  // Signals dock
226  QScrollArea* signal_scroll_area = new QScrollArea;
227 
228  // pass the signal map to the scope widget such that it can lookup signal keys and receive the corresponding object
229  // note, we cannot pass signal objects directly via drag-and-drop from the signal-widget, but we can drop the key.
230  _signal_collection_widget = new SignalCollectionWidget(_signal_helper);
231  connect(_signal_helper.get(), &SignalHelper::newSignal, _signal_collection_widget, &SignalCollectionWidget::addSignal);
232  connect(_signal_helper.get(), &SignalHelper::signalRemoved, _signal_collection_widget, &SignalCollectionWidget::removeTreeItem);
233  connect(_toolbox_widget, &ToolboxWidget::taskCompleted, _signal_collection_widget, &SignalCollectionWidget::moveToRecent);
234  connect(_signal_collection_widget, &SignalCollectionWidget::requestTaskParameterBackup, _toolbox_widget, &ToolboxWidget::restoreTaskParameters);
235  connect(_signal_collection_widget, &SignalCollectionWidget::taskRemoved, _toolbox_widget, &ToolboxWidget::removeFromTaskCache);
236 
237  signal_scroll_area->setWidget(_signal_collection_widget);
238  signal_scroll_area->setWidgetResizable(true);
239 
240  _dock_signal_widget = new QDockWidget(tr("Signals"), this);
241  _dock_signal_widget->setFloating(false);
242  _dock_signal_widget->setObjectName("SignalsDock");
243  _dock_signal_widget->setAllowedAreas(Qt::AllDockWidgetAreas);
244  _dock_signal_widget->setWidget(signal_scroll_area);
245  _dock_signal_widget->setFeatures(QDockWidget::AllDockWidgetFeatures);
246 
247  // update available signals
248  _toolbox_widget->getAvailableSignals();
249  _toolbox_widget->setSignalAutoRequest(true);
250 }
251 
252 void corboMainWindow::addMenuActions()
253 {
254  _menu_file->addAction(_action_rpc_server_address);
255  _menu_file->addSeparator();
256  _menu_file->addAction(_action_exit_program);
257 
258  _menu_view->addAction(_action_default_layout);
259 
260  _menu_parameters->addAction(_action_save_parameters_file);
261  _menu_parameters->addSeparator();
262  _menu_parameters->addAction(_action_load_parameters_file);
263  _menu_recent_parameters = _menu_parameters->addMenu(tr("Recent Parameters"));
264  for (std::size_t i = 0; i < _action_recent_files.size(); ++i) _menu_recent_parameters->addAction(_action_recent_files[i]);
265  updateRecentFileActions();
266 
267  _menu_scopes->addAction(_action_rescale_scope_axes);
268  _menu_scopes->addAction(_action_close_scopes);
269 
270  _menu_signals->addAction(_action_remove_signals);
271 
272  _menu_help->addAction(_action_about);
273 }
274 
275 void corboMainWindow::setDefaultLayout()
276 {
277  _dock_toolbox_widget->hide();
278  _dock_scope_widget->hide();
279  _dock_signal_widget->hide();
280  addDockWidget(Qt::LeftDockWidgetArea, _dock_toolbox_widget);
281  _dock_toolbox_widget->setFloating(false);
282  _dock_toolbox_widget->show();
283 
284  addDockWidget(Qt::RightDockWidgetArea, _dock_scope_widget);
285  _dock_scope_widget->setFloating(false);
286  _dock_scope_widget->show();
287 
288  addDockWidget(Qt::RightDockWidgetArea, _dock_signal_widget);
289  splitDockWidget(_dock_scope_widget, _dock_signal_widget, Qt::Horizontal);
290  _dock_signal_widget->setFloating(false);
291  _dock_signal_widget->show();
292 
293  // bring scope window to front
294  // _dock_scope_widget->raise();
295 }
296 
297 void corboMainWindow::readWindowSettings()
298 {
299  QSettings settings(OrganizationName, ApplicationName);
300  QVariant pos_var = settings.value("pos");
301  QVariant size_var = settings.value("size");
302  QVariant dock_var = settings.value("dock_locations");
303  QVariant rpc_server_address_var = settings.value("rpc_server_address");
304 
305  if (pos_var.canConvert<QPoint>()) move(pos_var.toPoint());
306  if (size_var.canConvert<QSize>()) resize(size_var.toSize());
307  if (dock_var.canConvert<QByteArray>()) restoreState(dock_var.toByteArray());
308  if (rpc_server_address_var.canConvert<QString>()) _rpc_server_address = rpc_server_address_var.toString();
309 }
310 
311 void corboMainWindow::writeWindowSettings()
312 {
313  QSettings settings(OrganizationName, ApplicationName);
314  settings.setValue("pos", pos());
315  settings.setValue("size", size());
316  settings.setValue("dock_locations", saveState());
317  settings.setValue("rpc_server_address", _rpc_server_address);
318 }
319 
320 void corboMainWindow::displayStatus(const QString& status, double duration_ms)
321 {
322  if (status.isEmpty())
323  {
324  displayStatusReady();
325  return;
326  }
327  statusBar()->showMessage(status);
328 
329  if (duration_ms > 0)
330  {
331  // start timer to reset to ready after the specified number of miliseconds
332  QTimer::singleShot(duration_ms, this, SLOT(displayStatusReady()));
333  }
334 }
335 
336 void corboMainWindow::updateRecentFileActions()
337 {
338  QSettings settings(OrganizationName, ApplicationName);
339  QStringList files = settings.value("recentFileList").toStringList();
340 
341  int num_recent_files = qMin(files.size(), (int)_action_recent_files.size());
342 
343  for (int i = 0; i < num_recent_files; ++i)
344  {
345  QString text = files[i];
346  _action_recent_files[i]->setText(text);
347  _action_recent_files[i]->setData(files[i]);
348  _action_recent_files[i]->setVisible(true);
349  }
350  for (int j = num_recent_files; j < (int)_action_recent_files.size(); ++j) _action_recent_files[j]->setVisible(false);
351 }
352 
353 void corboMainWindow::addToRecentFileAction(const QString& filename)
354 {
355  QSettings settings(OrganizationName, ApplicationName);
356  QStringList files = settings.value("recentFileList").toStringList();
357  files.removeAll(filename);
358  files.prepend(filename);
359  while (files.size() > _action_recent_files.size()) files.removeLast();
360 
361  settings.setValue("recentFileList", files);
362 
363  updateRecentFileActions();
364 }
365 
366 void corboMainWindow::aboutPopup()
367 {
368  QMessageBox::about(this, tr("About"), tr("<p><b>Institute of Control Theory and Systems Engineering, TU Dortmund University.</b></p>"
369  "<p>Written by Christoph Rösmann, <a "
370  "href=\"mailto:christoph.roesmann@tu-dortmund.de\">christoph.roesmann@tu-dortmund.de</a></p>"));
371 }
372 
373 void corboMainWindow::configureRpcWorker()
374 {
375  displayStatus("trying to connect with rpc server at " + _rpc_server_address);
376 
377  RpcConnectionWorker* rpc_worker = new RpcConnectionWorker(_rpc_client);
378  rpc_worker->moveToThread(&_rpc_worker_thread);
379  connect(this, SIGNAL(requestRpcServerAddress(const QString&)), rpc_worker, SLOT(connectRpc(const QString&)));
380  connect(&_rpc_worker_thread, SIGNAL(finished()), rpc_worker, SLOT(deleteLater()));
381  connect(rpc_worker, SIGNAL(connectionResult(std::shared_ptr<MasterServiceClient>)), this,
382  SLOT(updatedRpcClient(std::shared_ptr<MasterServiceClient>)));
383  connect(rpc_worker, &RpcConnectionWorker::connectionResult, this,
384  [this](std::shared_ptr<MasterServiceClient> /*client*/) { displayStatusReady(); });
385 
386  _rpc_worker_thread.start(QThread::LowPriority);
387 
388  emit requestRpcServerAddress(_rpc_server_address);
389 }
390 
391 void corboMainWindow::updatedRpcClient(std::shared_ptr<MasterServiceClient> client)
392 {
393  if (client)
394  {
395  // _rpc_client = client;
396  // _toolbox_widget->setRpcClient(_rpc_client);
397  _toolbox_widget->requestParametersFromService();
398  }
399 }
400 
401 } // namespace gui
402 } // namespace corbo
corbo::gui::ScopeCollectionWidget
Definition: scope_collection_widget.h:82
Eigen::Horizontal
@ Horizontal
Definition: Constants.h:268
corbo
Definition: communication/include/corbo-communication/utilities.h:37
corbo::gui::corboMainWindow::createMenus
void createMenus()
Definition: main_window.cpp:207
corbo::gui::corboMainWindow::createDockWindows
void createDockWindows()
Definition: main_window.cpp:234
corbo::gui::corboMainWindow::createStatusBar
void createStatusBar()
Definition: main_window.cpp:232
corbo::gui::corboMainWindow::createActions
void createActions()
Definition: main_window.cpp:138
corbo::gui::OrganizationName
const QString OrganizationName
Definition: main_window.h:94
corbo::gui::corboMainWindow::_rpc_client
std::shared_ptr< MasterServiceClient > _rpc_client
Definition: main_window.h:151
relicense.filename
filename
Definition: relicense.py:57
corbo::gui::corboMainWindow::_central_widget
QWidget * _central_widget
Definition: main_window.h:153
int
return int(ret)+1
corbo::gui::corboMainWindow::setDefaultLayout
void setDefaultLayout()
Definition: main_window.cpp:319
corbo::gui::ApplicationName
const QString ApplicationName
Definition: main_window.h:95
corbo::gui::corboMainWindow::_signal_helper
SignalHelper::Ptr _signal_helper
Definition: main_window.h:150
corbo::gui::corboMainWindow::readWindowSettings
void readWindowSettings()
Definition: main_window.cpp:341
corbo::gui::corboMainWindow::addMenuActions
void addMenuActions()
Definition: main_window.cpp:296
utility::tuple::size
static constexpr size_t size(Tuple< Args... > &)
Provides access to the number of elements in a tuple as a compile-time constant expression.
Definition: TensorSyclTuple.h:143
corbo::gui::corboMainWindow::configureRpcWorker
void configureRpcWorker()
Definition: main_window.cpp:417
corbo::gui::corboMainWindow::corboMainWindow
corboMainWindow(QWidget *parent=0)
Definition: main_window.cpp:88
corbo::ok
bool ok()
global method to check whether to proceed or cancel the current action
Definition: global.cpp:54
relicense.text
text
Definition: relicense.py:59
corbo::gui::SignalCollectionWidget
Definition: signal_collection_widget.h:91
main_window.h


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Wed Mar 2 2022 00:05:53