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 
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 
87 {
88  _rpc_worker_thread.quit();
89  _rpc_worker_thread.wait();
90 }
91 
92 void corboMainWindow::closeEvent(QCloseEvent* event) { writeWindowSettings(); }
93 
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);
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  {
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 
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 
189 
191 {
192  // Toolbox dock
193  QScrollArea* toolbox_scroll_area = new QScrollArea;
195  toolbox_scroll_area->setWidget(_toolbox_widget);
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);
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;
210  // scope_scroll_area->setWidget(_scope_collection_widget);
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.
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
250 }
251 
253 {
255  _menu_file->addSeparator();
256  _menu_file->addAction(_action_exit_program);
257 
259 
261  _menu_parameters->addSeparator();
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]);
266 
269 
271 
272  _menu_help->addAction(_action_about);
273 }
274 
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);
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 
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 
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  {
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 
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 
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 
364 }
365 
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 
374 {
375  displayStatus("trying to connect with rpc server at " + _rpc_server_address);
376 
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 
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);
398  }
399 }
400 
401 } // namespace gui
402 } // namespace corbo
ToolboxWidget * _toolbox_widget
Definition: main_window.h:134
QAction * _action_rpc_server_address
Definition: main_window.h:117
QDockWidget * _dock_toolbox_widget
Definition: main_window.h:130
corboMainWindow(QWidget *parent=0)
Definition: main_window.cpp:44
return int(ret)+1
void updatedRpcClient(std::shared_ptr< MasterServiceClient > client)
ScopeCollectionWidget * _scope_collection_widget
Definition: main_window.h:136
SignalHelper::Ptr _signal_helper
Definition: main_window.h:106
void parameterLoadedFromFile(const QString &filename)
void signalRemoved(const QString &key, int value_idx)
const QString OrganizationName
Definition: main_window.h:50
static constexpr size_t size(Tuple< Args... > &)
Provides access to the number of elements in a tuple as a compile-time constant expression.
void newSignal(const QString &key, SignalData &signal_data)
QAction * _action_rescale_scope_axes
Definition: main_window.h:115
void connectionResult(std::shared_ptr< MasterServiceClient > client)
QAction * _action_load_parameters_file
Definition: main_window.h:112
QDockWidget * _dock_signal_widget
Definition: main_window.h:131
void newSeries(int task_id)
void requestRpcServerAddress(const QString &address)
void addMeasurement(const QString &key, Measurement::ConstPtr measurement, SignalHelper::SignalData &signal_data, bool first)
void addSignal(const QString &key, SignalHelper::SignalData &signal_data)
QAction * _action_save_parameters_file
Definition: main_window.h:111
void newMeasurement(const QString &key, Measurement::ConstPtr measurement, SignalData &signal_data, bool first)
void addToRecentFileAction(const QString &filename)
void closeEvent(QCloseEvent *event) override
Definition: main_window.cpp:92
void removeSignal(const QString &key, int value_idx)
void restoreTaskParameters(int task_id)
QDockWidget * _dock_scope_widget
Definition: main_window.h:132
const QString ApplicationName
Definition: main_window.h:51
SignalCollectionWidget * _signal_collection_widget
Definition: main_window.h:135
void removeTreeItem(const QString &key, int value_idx)
void displayStatus(const QString &status, double duration_ms=0)
void taskCompleted(const QDateTime &time)
void setSignalAutoRequest(bool active)
bool ok()
global method to check whether to proceed or cancel the current action
Definition: global.cpp:32
void removeFromTaskCache(int task_id)
void requestTaskParameterBackup(int task_id)
GUI main window.
Definition: main_window.h:68
std::array< QAction *, 6 > _action_recent_files
Definition: main_window.h:120
void parameterSavedToFile(const QString &filename)
std::shared_ptr< MasterServiceClient > _rpc_client
Definition: main_window.h:107


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Mon Feb 28 2022 22:07:00