QCodeEditor: Update to current cpeditor/QCodeEditor fork, commit ed1196a
[smuview.git] / src / mainwindow.cpp
blobe29422f1b5a1a088325109224972e417b0db72a7
1 /*
2 * This file is part of the SmuView project.
4 * Copyright (C) 2017-2021 Frank Stettner <frank-stettner@gmx.net>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <memory>
21 #include <string>
22 #include <utility>
24 #include <QApplication>
25 #include <QDebug>
26 #include <QDockWidget>
27 #include <QFontDatabase>
28 #include <QHBoxLayout>
29 #include <QLabel>
30 #include <QMessageBox>
31 #include <QSizePolicy>
32 #include <QVBoxLayout>
34 #include "mainwindow.hpp"
35 #include "config.h"
36 #include "src/devicemanager.hpp"
37 #include "src/session.hpp"
38 #include "src/settingsmanager.hpp"
39 #include "src/util.hpp"
40 #include "src/data/basesignal.hpp"
41 #include "src/data/analogtimesignal.hpp"
42 #include "src/devices/basedevice.hpp"
43 #include "src/devices/configurable.hpp"
44 #include "src/devices/deviceutil.hpp"
45 #include "src/devices/hardwaredevice.hpp"
46 #include "src/devices/measurementdevice.hpp"
47 #include "src/devices/sourcesinkdevice.hpp"
48 #include "src/devices/userdevice.hpp"
49 #include "src/channels/basechannel.hpp"
50 #include "src/python/smuscriptrunner.hpp"
51 #include "src/ui/dialogs/connectdialog.hpp"
52 #include "src/ui/tabs/basetab.hpp"
53 #include "src/ui/tabs/devicetab.hpp"
54 #include "src/ui/tabs/smuscripttab.hpp"
55 #include "src/ui/tabs/tabhelper.hpp"
56 #include "src/ui/tabs/welcometab.hpp"
57 #include "src/ui/views/devicesview.hpp"
58 #include "src/ui/views/smuscripttreeview.hpp"
60 using std::make_pair;
61 using std::shared_ptr;
62 using std::string;
64 Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
65 Q_DECLARE_METATYPE(std::shared_ptr<sv::devices::BaseDevice>)
66 Q_DECLARE_METATYPE(std::shared_ptr<sv::channels::BaseChannel>)
67 Q_DECLARE_METATYPE(std::shared_ptr<sv::data::BaseSignal>)
69 namespace sv
72 MainWindow::MainWindow(DeviceManager &device_manager,
73 shared_ptr<Session> session, QWidget *parent) :
74 QMainWindow(parent),
75 device_manager_(device_manager),
76 session_(session)
78 qRegisterMetaType<util::Timestamp>("util::Timestamp");
79 qRegisterMetaType<uint64_t>("uint64_t");
80 qRegisterMetaType<std::string>("std::string");
81 qRegisterMetaType<Qt::DockWidgetArea>("Qt::DockWidgetArea");
82 qRegisterMetaType<shared_ptr<devices::BaseDevice>>("shared_ptr<sv::devices::BaseDevice>");
83 qRegisterMetaType<shared_ptr<devices::Configurable>>("shared_ptr<sv::devices::Configurable>");
84 qRegisterMetaType<shared_ptr<devices::HardwareDevice>>("shared_ptr<sv::devices::HardwareDevice>");
85 qRegisterMetaType<shared_ptr<channels::BaseChannel>>("shared_ptr<sv::channels::BaseChannel>");
86 qRegisterMetaType<shared_ptr<data::BaseSignal>>("shared_ptr<sv::data::BaseSignal>");
87 qRegisterMetaType<shared_ptr<data::AnalogTimeSignal>>("shared_ptr<sv::data::AnalogTimeSignal>");
88 qRegisterMetaType<devices::ConfigKey>("devices::ConfigKey");
90 // Add embedded mono space font for the value display.
91 QFontDatabase::addApplicationFont(":/fonts/DejaVuSansMono.ttf");
93 session_->set_main_window(this);
95 setup_ui();
96 if (SettingsManager::restore_settings())
97 restore_settings();
98 connect_signals();
99 init_device_tabs();
102 MainWindow::~MainWindow()
106 void MainWindow::add_tab(ui::tabs::BaseTab *tab_window)
108 // NOTE: This must be before QTabWidget::addTab() and
109 // QTabWidget::setCurrentIndex() otherwise the tab_window is null for
110 // the SmuScriptTab after inserting into tab_window_map_.
111 tab_window_map_.insert(make_pair(tab_window->id(), tab_window));
113 int index = tab_widget_->addTab(tab_window, tab_window->title());
114 tab_widget_->setCurrentIndex(index);
117 ui::tabs::DeviceTab *MainWindow::add_device_tab(
118 shared_ptr<sv::devices::BaseDevice> device)
120 auto *tab = ui::tabs::tabhelper::get_tab_for_device(*session_, device);
121 add_tab(tab);
123 // Connect device error handler to show a message box
124 connect(device.get(), &sv::devices::BaseDevice::device_error,
125 this, &MainWindow::error_handler);
127 return tab;
130 ui::tabs::WelcomeTab *MainWindow::add_welcome_tab()
132 auto *tab = new ui::tabs::WelcomeTab(*session_);
133 add_tab(tab);
134 return tab;
137 ui::tabs::SmuScriptTab *MainWindow::add_smuscript_tab(const string &file_name)
139 auto *tab = new ui::tabs::SmuScriptTab(*session_, file_name);
140 add_tab(tab);
141 return tab;
144 void MainWindow::remove_tab(const string &tab_id)
146 if (tab_window_map_.count(tab_id) == 0)
147 return;
149 remove_tab(tab_widget_->indexOf(tab_window_map_[tab_id]));
152 void MainWindow::remove_tab(int tab_index)
154 QWidget *tab_window = tab_widget_->widget(tab_index);
156 tab_widget_->removeTab(tab_index);
158 for (const auto &pair : tab_window_map_) {
159 if (pair.second == tab_window) {
160 tab_window_map_.erase(pair.first);
161 break;
165 // Call close() of the tab window to save settings (*Tab::closeEvent())
166 tab_window->close();
167 tab_window->deleteLater();
169 if (tab_window_map_.empty()) {
170 // When there are no more tabs, display the WelcomeTab
171 add_welcome_tab();
175 void MainWindow::change_tab_icon(const string &tab_id, const QIcon &icon)
177 int tab_index = tab_widget_->indexOf(tab_window_map_[tab_id]);
178 tab_widget_->setTabIcon(tab_index, icon);
181 void MainWindow::change_tab_title(const string &tab_id, const QString &title)
183 int tab_index = tab_widget_->indexOf(tab_window_map_[tab_id]);
184 tab_widget_->setTabText(tab_index, title);
187 ui::tabs::BaseTab *MainWindow::get_tab_from_tab_id(const string &id)
189 if (tab_window_map_.count(id) == 0)
190 return nullptr;
192 return tab_window_map_[id];
195 void MainWindow::setup_ui()
197 QIcon mainIcon;
198 mainIcon.addFile(QStringLiteral(":/icons/smuview.ico"),
199 QSize(), QIcon::Normal, QIcon::Off);
200 this->setWindowIcon(mainIcon);
202 QString window_title = QString("%1 %2").
203 arg(tr("SmuView"), SV_VERSION_STRING);
204 this->setWindowTitle(window_title);
206 QHBoxLayout *centralLayout = new QHBoxLayout();
207 centralLayout->setContentsMargins(2, 2, 2, 2);
208 central_widget_ = new QWidget();
209 central_widget_->setLayout(centralLayout);
211 // Tab Widget
212 tab_widget_ = new QTabWidget();
213 tab_widget_->setTabsClosable(true);
214 connect(tab_widget_, &QTabWidget::tabCloseRequested,
215 this, &MainWindow::on_tab_close_requested);
216 centralLayout->addWidget(tab_widget_);
218 this->setCentralWidget(central_widget_);
220 // DeviceTreeView Dock
221 devices_view_ = new ui::views::DevicesView(*session_);
222 devices_view_->setSizePolicy(
223 QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
225 // A layout must be set to the central widget of the main window
226 // before dev_dock->setWidget() is called.
227 QDockWidget* dev_dock = new QDockWidget(devices_view_->title());
228 dev_dock->setObjectName("dev_doc");
229 dev_dock->setAllowedAreas(Qt::AllDockWidgetAreas);
230 dev_dock->setContextMenuPolicy(Qt::PreventContextMenu);
231 dev_dock->setFeatures(QDockWidget::DockWidgetMovable |
232 QDockWidget::DockWidgetFloatable);
233 dev_dock->setWidget(devices_view_);
234 this->addDockWidget(Qt::LeftDockWidgetArea, dev_dock);
236 // This fixes a qt bug. See: https://bugreports.qt.io/browse/QTBUG-65592
237 this->resizeDocks({dev_dock}, {40}, Qt::Horizontal);
239 // SmuScript Tree Dock
240 smu_script_tree_view_ = new ui::views::SmuScriptTreeView(*session_);
242 QDockWidget* script_dock = new QDockWidget(smu_script_tree_view_->title());
243 script_dock->setObjectName("script_dock");
244 script_dock->setAllowedAreas(Qt::AllDockWidgetAreas);
245 script_dock->setContextMenuPolicy(Qt::PreventContextMenu);
246 script_dock->setFeatures(QDockWidget::DockWidgetMovable |
247 QDockWidget::DockWidgetFloatable);
248 script_dock->setWidget(smu_script_tree_view_);
249 this->tabifyDockWidget(dev_dock, script_dock);
251 // Select device tree dock tab
252 dev_dock->show();
253 dev_dock->raise();
256 void MainWindow::init_device_tabs()
258 if (device_manager_.user_spec_devices().empty()) {
259 // Display the WelcomeTab if no DeviceTabs will be opened, because
260 // without a tab in the QTabWidget the main window looks so empty...
261 add_welcome_tab();
262 return;
265 for (const auto &device : device_manager_.user_spec_devices()) {
266 add_device_tab(device);
270 void MainWindow::connect_signals()
272 // Connect error handlers
273 connect(session_->smu_script_runner().get(), &python::SmuScriptRunner::script_error,
274 this, &MainWindow::error_handler);
277 void MainWindow::save_settings()
279 QSettings settings;
281 smu_script_tree_view_->save_settings(settings);
283 settings.beginGroup("MainWindow");
284 settings.setValue("geometry", saveGeometry());
285 settings.setValue("state", saveState());
286 settings.endGroup();
289 void MainWindow::restore_settings()
291 QSettings settings;
293 // Restore main window stuff
294 settings.beginGroup("MainWindow");
295 if (settings.contains("geometry")) {
296 restoreGeometry(settings.value("geometry").toByteArray());
297 restoreState(settings.value("state").toByteArray());
299 else
300 resize(1000, 720);
301 settings.endGroup();
304 void MainWindow::error_handler(
305 const std::string &sender, const std::string &msg)
307 QMessageBox msg_box(this);
308 msg_box.setText(QString::fromStdString(sender));
309 msg_box.setInformativeText(QString::fromStdString(msg));
310 msg_box.setStandardButtons(QMessageBox::Ok);
311 msg_box.setIcon(QMessageBox::Critical);
312 msg_box.exec();
315 void MainWindow::on_tab_close_requested(int tab_index)
317 auto *tab_window = (ui::tabs::BaseTab *)tab_widget_->widget(tab_index);
318 if (tab_window->request_close())
319 remove_tab(tab_index);
322 void MainWindow::closeEvent(QCloseEvent *event)
324 for (const auto &tab_window_pair : this->tab_window_map_) {
325 // Call close() of the tab window to save settings (*Tab::closeEvent())
326 tab_window_pair.second->close();
329 save_settings();
330 event->accept();
333 } // namespace sv